Skip to main content

tiff_reader/
tag.rs

1use crate::error::{Error, Result};
2use crate::header::ByteOrder;
3use crate::io::Cursor;
4use crate::source::TiffSource;
5
6/// TIFF data type codes.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum TagType {
9    Byte,      // 1
10    Ascii,     // 2
11    Short,     // 3
12    Long,      // 4
13    Rational,  // 5
14    SByte,     // 6
15    Undefined, // 7
16    SShort,    // 8
17    SLong,     // 9
18    SRational, // 10
19    Float,     // 11
20    Double,    // 12
21    Long8,     // 16 (BigTIFF)
22    SLong8,    // 17 (BigTIFF)
23    Ifd8,      // 18 (BigTIFF)
24    Unknown(u16),
25}
26
27impl TagType {
28    pub fn from_code(code: u16) -> Self {
29        match code {
30            1 => Self::Byte,
31            2 => Self::Ascii,
32            3 => Self::Short,
33            4 => Self::Long,
34            5 => Self::Rational,
35            6 => Self::SByte,
36            7 => Self::Undefined,
37            8 => Self::SShort,
38            9 => Self::SLong,
39            10 => Self::SRational,
40            11 => Self::Float,
41            12 => Self::Double,
42            16 => Self::Long8,
43            17 => Self::SLong8,
44            18 => Self::Ifd8,
45            _ => Self::Unknown(code),
46        }
47    }
48
49    /// Size in bytes of a single element of this type.
50    pub fn element_size(&self) -> usize {
51        match self {
52            Self::Byte | Self::Ascii | Self::SByte | Self::Undefined => 1,
53            Self::Short | Self::SShort => 2,
54            Self::Long | Self::SLong | Self::Float => 4,
55            Self::Rational
56            | Self::SRational
57            | Self::Double
58            | Self::Long8
59            | Self::SLong8
60            | Self::Ifd8 => 8,
61            Self::Unknown(_) => 1,
62        }
63    }
64}
65
66/// A parsed TIFF tag.
67#[derive(Debug, Clone)]
68pub struct Tag {
69    pub code: u16,
70    pub tag_type: TagType,
71    pub count: u64,
72    pub value: TagValue,
73}
74
75/// Decoded tag value.
76#[derive(Debug, Clone)]
77pub enum TagValue {
78    Byte(Vec<u8>),
79    Ascii(String),
80    Short(Vec<u16>),
81    Long(Vec<u32>),
82    Rational(Vec<[u32; 2]>),
83    SByte(Vec<i8>),
84    Undefined(Vec<u8>),
85    SShort(Vec<i16>),
86    SLong(Vec<i32>),
87    SRational(Vec<[i32; 2]>),
88    Float(Vec<f32>),
89    Double(Vec<f64>),
90    Long8(Vec<u64>),
91    SLong8(Vec<i64>),
92}
93
94impl TagValue {
95    /// Extract a single u16 value.
96    pub fn as_u16(&self) -> Option<u16> {
97        match self {
98            Self::Short(v) => v.first().copied(),
99            Self::Byte(v) => v.first().map(|&b| b as u16),
100            Self::Long(v) => v.first().map(|&l| l as u16),
101            _ => None,
102        }
103    }
104
105    /// Extract a single u32 value.
106    pub fn as_u32(&self) -> Option<u32> {
107        match self {
108            Self::Long(v) => v.first().copied(),
109            Self::Short(v) => v.first().map(|&s| s as u32),
110            Self::Long8(v) => v.first().map(|&l| l as u32),
111            _ => None,
112        }
113    }
114
115    /// Extract a single u64 value.
116    pub fn as_u64(&self) -> Option<u64> {
117        match self {
118            Self::Long8(v) => v.first().copied(),
119            Self::Long(v) => v.first().map(|&l| l as u64),
120            Self::Short(v) => v.first().map(|&s| s as u64),
121            _ => None,
122        }
123    }
124
125    /// Extract a single f64 value.
126    pub fn as_f64(&self) -> Option<f64> {
127        match self {
128            Self::Double(v) => v.first().copied(),
129            Self::Float(v) => v.first().map(|&f| f as f64),
130            Self::Long(v) => v.first().map(|&l| l as f64),
131            Self::Short(v) => v.first().map(|&s| s as f64),
132            _ => None,
133        }
134    }
135
136    /// Extract as a string.
137    pub fn as_str(&self) -> Option<&str> {
138        match self {
139            Self::Ascii(s) => Some(s.as_str()),
140            _ => None,
141        }
142    }
143
144    /// Extract raw bytes for byte-oriented tag payloads.
145    pub fn as_bytes(&self) -> Option<&[u8]> {
146        match self {
147            Self::Byte(v) | Self::Undefined(v) => Some(v.as_slice()),
148            _ => None,
149        }
150    }
151
152    /// Extract as a slice of f64 values.
153    pub fn as_f64_vec(&self) -> Option<Vec<f64>> {
154        match self {
155            Self::Double(v) => Some(v.clone()),
156            Self::Float(v) => Some(v.iter().map(|&f| f as f64).collect()),
157            _ => None,
158        }
159    }
160
161    /// Extract a value list as unsigned offsets/counts.
162    pub fn as_u64_vec(&self) -> Option<Vec<u64>> {
163        match self {
164            Self::Byte(v) => Some(v.iter().map(|&x| x as u64).collect()),
165            Self::Short(v) => Some(v.iter().map(|&x| x as u64).collect()),
166            Self::Long(v) => Some(v.iter().map(|&x| x as u64).collect()),
167            Self::Long8(v) => Some(v.clone()),
168            _ => None,
169        }
170    }
171
172    /// Extract a SHORT array without cloning when possible.
173    pub fn as_u16_slice(&self) -> Option<&[u16]> {
174        match self {
175            Self::Short(v) => Some(v.as_slice()),
176            _ => None,
177        }
178    }
179}
180
181impl Tag {
182    /// Parse a classic TIFF tag entry (12-byte IFD entry).
183    pub fn parse_classic(
184        code: u16,
185        type_code: u16,
186        count: u64,
187        value_offset_bytes: &[u8],
188        source: &dyn TiffSource,
189        byte_order: ByteOrder,
190    ) -> Result<Self> {
191        let tag_type = TagType::from_code(type_code);
192        let total_size = value_len(code, count, tag_type.element_size())?;
193
194        let owned;
195        let value_bytes = if total_size <= 4 {
196            &value_offset_bytes[..total_size]
197        } else {
198            let offset = match byte_order {
199                ByteOrder::LittleEndian => {
200                    u32::from_le_bytes(value_offset_bytes.try_into().unwrap())
201                }
202                ByteOrder::BigEndian => u32::from_be_bytes(value_offset_bytes.try_into().unwrap()),
203            } as u64;
204            owned = read_value_bytes(source, offset, total_size)?;
205            owned.as_slice()
206        };
207
208        let value = decode_value(&tag_type, count, value_bytes, byte_order)?;
209        Ok(Self {
210            code,
211            tag_type,
212            count,
213            value,
214        })
215    }
216
217    /// Parse a BigTIFF tag entry (20-byte IFD entry).
218    pub fn parse_bigtiff(
219        code: u16,
220        type_code: u16,
221        count: u64,
222        value_offset_bytes: &[u8],
223        source: &dyn TiffSource,
224        byte_order: ByteOrder,
225    ) -> Result<Self> {
226        let tag_type = TagType::from_code(type_code);
227        let total_size = value_len(code, count, tag_type.element_size())?;
228
229        let owned;
230        let value_bytes = if total_size <= 8 {
231            &value_offset_bytes[..total_size]
232        } else {
233            let offset = match byte_order {
234                ByteOrder::LittleEndian => {
235                    u64::from_le_bytes(value_offset_bytes.try_into().unwrap())
236                }
237                ByteOrder::BigEndian => u64::from_be_bytes(value_offset_bytes.try_into().unwrap()),
238            };
239            owned = read_value_bytes(source, offset, total_size)?;
240            owned.as_slice()
241        };
242
243        let value = decode_value(&tag_type, count, value_bytes, byte_order)?;
244        Ok(Self {
245            code,
246            tag_type,
247            count,
248            value,
249        })
250    }
251}
252
253fn read_value_bytes(source: &dyn TiffSource, offset: u64, len: usize) -> Result<Vec<u8>> {
254    if let Some(data) = source.as_slice() {
255        return Ok(slice_at(data, offset, len)?.to_vec());
256    }
257    source.read_exact_at(offset, len)
258}
259
260fn value_len(tag: u16, count: u64, element_size: usize) -> Result<usize> {
261    let count = usize::try_from(count).map_err(|_| Error::InvalidTagValue {
262        tag,
263        reason: "value count does not fit in memory".into(),
264    })?;
265    count
266        .checked_mul(element_size)
267        .ok_or_else(|| Error::InvalidTagValue {
268            tag,
269            reason: "value byte length overflows usize".into(),
270        })
271}
272
273fn slice_at(data: &[u8], offset: u64, len: usize) -> Result<&[u8]> {
274    let start = usize::try_from(offset).map_err(|_| Error::OffsetOutOfBounds {
275        offset,
276        length: len as u64,
277        data_len: data.len() as u64,
278    })?;
279    let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
280        offset,
281        length: len as u64,
282        data_len: data.len() as u64,
283    })?;
284    if end > data.len() {
285        return Err(Error::OffsetOutOfBounds {
286            offset,
287            length: len as u64,
288            data_len: data.len() as u64,
289        });
290    }
291    Ok(&data[start..end])
292}
293
294fn decode_value(
295    tag_type: &TagType,
296    count: u64,
297    bytes: &[u8],
298    byte_order: ByteOrder,
299) -> Result<TagValue> {
300    let mut cursor = Cursor::new(bytes, byte_order);
301    let n = count as usize;
302
303    Ok(match tag_type {
304        TagType::Byte | TagType::Unknown(_) => TagValue::Byte(cursor.read_bytes(n)?.to_vec()),
305        TagType::Ascii => {
306            let raw = cursor.read_bytes(n)?;
307            let s = String::from_utf8_lossy(raw)
308                .trim_end_matches('\0')
309                .to_string();
310            TagValue::Ascii(s)
311        }
312        TagType::Short => {
313            let mut v = Vec::with_capacity(n);
314            for _ in 0..n {
315                v.push(cursor.read_u16()?);
316            }
317            TagValue::Short(v)
318        }
319        TagType::Long => {
320            let mut v = Vec::with_capacity(n);
321            for _ in 0..n {
322                v.push(cursor.read_u32()?);
323            }
324            TagValue::Long(v)
325        }
326        TagType::Rational => {
327            let mut v = Vec::with_capacity(n);
328            for _ in 0..n {
329                let num = cursor.read_u32()?;
330                let den = cursor.read_u32()?;
331                v.push([num, den]);
332            }
333            TagValue::Rational(v)
334        }
335        TagType::SByte => {
336            let raw = cursor.read_bytes(n)?;
337            TagValue::SByte(raw.iter().map(|&b| b as i8).collect())
338        }
339        TagType::Undefined => TagValue::Undefined(cursor.read_bytes(n)?.to_vec()),
340        TagType::SShort => {
341            let mut v = Vec::with_capacity(n);
342            for _ in 0..n {
343                v.push(cursor.read_u16()? as i16);
344            }
345            TagValue::SShort(v)
346        }
347        TagType::SLong => {
348            let mut v = Vec::with_capacity(n);
349            for _ in 0..n {
350                v.push(cursor.read_u32()? as i32);
351            }
352            TagValue::SLong(v)
353        }
354        TagType::SRational => {
355            let mut v = Vec::with_capacity(n);
356            for _ in 0..n {
357                let num = cursor.read_u32()? as i32;
358                let den = cursor.read_u32()? as i32;
359                v.push([num, den]);
360            }
361            TagValue::SRational(v)
362        }
363        TagType::Float => {
364            let mut v = Vec::with_capacity(n);
365            for _ in 0..n {
366                let bits = cursor.read_u32()?;
367                v.push(f32::from_bits(bits));
368            }
369            TagValue::Float(v)
370        }
371        TagType::Double => {
372            let mut v = Vec::with_capacity(n);
373            for _ in 0..n {
374                v.push(cursor.read_f64()?);
375            }
376            TagValue::Double(v)
377        }
378        TagType::Long8 | TagType::Ifd8 => {
379            let mut v = Vec::with_capacity(n);
380            for _ in 0..n {
381                v.push(cursor.read_u64()?);
382            }
383            TagValue::Long8(v)
384        }
385        TagType::SLong8 => {
386            let mut v = Vec::with_capacity(n);
387            for _ in 0..n {
388                v.push(cursor.read_u64()? as i64);
389            }
390            TagValue::SLong8(v)
391        }
392    })
393}