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
6pub use tiff_core::{Tag, TagType, TagValue};
7
8/// Parse a classic TIFF tag entry (12-byte IFD entry).
9pub fn parse_tag_classic(
10    code: u16,
11    type_code: u16,
12    count: u64,
13    value_offset_bytes: &[u8],
14    source: &dyn TiffSource,
15    byte_order: ByteOrder,
16    max_value_bytes: usize,
17) -> Result<Tag> {
18    let tag_type = TagType::from_code(type_code);
19    let total_size = checked_value_len_for_type(code, count, tag_type, max_value_bytes)?;
20
21    let owned;
22    let value_bytes = if total_size <= 4 {
23        &value_offset_bytes[..total_size]
24    } else {
25        let offset = match byte_order {
26            ByteOrder::LittleEndian => u32::from_le_bytes(value_offset_bytes.try_into().unwrap()),
27            ByteOrder::BigEndian => u32::from_be_bytes(value_offset_bytes.try_into().unwrap()),
28        } as u64;
29        owned = read_value_bytes(source, offset, total_size)?;
30        owned.as_slice()
31    };
32
33    let value = decode_value(&tag_type, count, value_bytes, byte_order)?;
34    Ok(Tag {
35        code,
36        tag_type,
37        count,
38        value,
39    })
40}
41
42/// Parse a BigTIFF tag entry (20-byte IFD entry).
43pub fn parse_tag_bigtiff(
44    code: u16,
45    type_code: u16,
46    count: u64,
47    value_offset_bytes: &[u8],
48    source: &dyn TiffSource,
49    byte_order: ByteOrder,
50    max_value_bytes: usize,
51) -> Result<Tag> {
52    let tag_type = TagType::from_code(type_code);
53    let total_size = checked_value_len_for_type(code, count, tag_type, max_value_bytes)?;
54
55    let owned;
56    let value_bytes = if total_size <= 8 {
57        &value_offset_bytes[..total_size]
58    } else {
59        let offset = match byte_order {
60            ByteOrder::LittleEndian => u64::from_le_bytes(value_offset_bytes.try_into().unwrap()),
61            ByteOrder::BigEndian => u64::from_be_bytes(value_offset_bytes.try_into().unwrap()),
62        };
63        owned = read_value_bytes(source, offset, total_size)?;
64        owned.as_slice()
65    };
66
67    let value = decode_value(&tag_type, count, value_bytes, byte_order)?;
68    Ok(Tag {
69        code,
70        tag_type,
71        count,
72        value,
73    })
74}
75
76pub(crate) fn checked_tag_value_byte_len(
77    tag: u16,
78    type_code: u16,
79    count: u64,
80    max_value_bytes: usize,
81) -> Result<usize> {
82    checked_value_len_for_type(tag, count, TagType::from_code(type_code), max_value_bytes)
83}
84
85fn checked_value_len_for_type(
86    tag: u16,
87    count: u64,
88    tag_type: TagType,
89    max_value_bytes: usize,
90) -> Result<usize> {
91    let total_size = value_len(tag, count, tag_type.element_size())?;
92    validate_value_budget(tag, total_size, max_value_bytes)?;
93    Ok(total_size)
94}
95
96fn read_value_bytes(source: &dyn TiffSource, offset: u64, len: usize) -> Result<Vec<u8>> {
97    if let Some(data) = source.as_slice() {
98        return Ok(slice_at(data, offset, len)?.to_vec());
99    }
100    source.read_exact_at(offset, len)
101}
102
103fn value_len(tag: u16, count: u64, element_size: usize) -> Result<usize> {
104    let count = usize::try_from(count).map_err(|_| Error::InvalidTagValue {
105        tag,
106        reason: "value count does not fit in memory".into(),
107    })?;
108    count
109        .checked_mul(element_size)
110        .ok_or_else(|| Error::InvalidTagValue {
111            tag,
112            reason: "value byte length overflows usize".into(),
113        })
114}
115
116fn validate_value_budget(tag: u16, total_size: usize, max_value_bytes: usize) -> Result<()> {
117    if total_size > max_value_bytes {
118        return Err(Error::InvalidTagValue {
119            tag,
120            reason: format!(
121                "value byte length {total_size} exceeds parse budget {max_value_bytes}"
122            ),
123        });
124    }
125    Ok(())
126}
127
128fn slice_at(data: &[u8], offset: u64, len: usize) -> Result<&[u8]> {
129    let start = usize::try_from(offset).map_err(|_| Error::OffsetOutOfBounds {
130        offset,
131        length: len as u64,
132        data_len: data.len() as u64,
133    })?;
134    let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
135        offset,
136        length: len as u64,
137        data_len: data.len() as u64,
138    })?;
139    if end > data.len() {
140        return Err(Error::OffsetOutOfBounds {
141            offset,
142            length: len as u64,
143            data_len: data.len() as u64,
144        });
145    }
146    Ok(&data[start..end])
147}
148
149fn decode_value(
150    tag_type: &TagType,
151    count: u64,
152    bytes: &[u8],
153    byte_order: ByteOrder,
154) -> Result<TagValue> {
155    let mut cursor = Cursor::new(bytes, byte_order);
156    let n =
157        usize::try_from(count).map_err(|_| Error::Other("tag value count exceeds usize".into()))?;
158
159    Ok(match tag_type {
160        TagType::Byte | TagType::Unknown(_) => TagValue::Byte(cursor.read_bytes(n)?.to_vec()),
161        TagType::Ascii => {
162            let raw = cursor.read_bytes(n)?;
163            let s = String::from_utf8_lossy(raw)
164                .trim_end_matches('\0')
165                .to_string();
166            TagValue::Ascii(s)
167        }
168        TagType::Short => {
169            let mut v = Vec::with_capacity(n);
170            for _ in 0..n {
171                v.push(cursor.read_u16()?);
172            }
173            TagValue::Short(v)
174        }
175        TagType::Long => {
176            let mut v = Vec::with_capacity(n);
177            for _ in 0..n {
178                v.push(cursor.read_u32()?);
179            }
180            TagValue::Long(v)
181        }
182        TagType::Rational => {
183            let mut v = Vec::with_capacity(n);
184            for _ in 0..n {
185                let num = cursor.read_u32()?;
186                let den = cursor.read_u32()?;
187                v.push([num, den]);
188            }
189            TagValue::Rational(v)
190        }
191        TagType::SByte => {
192            let raw = cursor.read_bytes(n)?;
193            TagValue::SByte(raw.iter().map(|&b| b as i8).collect())
194        }
195        TagType::Undefined => TagValue::Undefined(cursor.read_bytes(n)?.to_vec()),
196        TagType::SShort => {
197            let mut v = Vec::with_capacity(n);
198            for _ in 0..n {
199                v.push(cursor.read_u16()? as i16);
200            }
201            TagValue::SShort(v)
202        }
203        TagType::SLong => {
204            let mut v = Vec::with_capacity(n);
205            for _ in 0..n {
206                v.push(cursor.read_u32()? as i32);
207            }
208            TagValue::SLong(v)
209        }
210        TagType::SRational => {
211            let mut v = Vec::with_capacity(n);
212            for _ in 0..n {
213                let num = cursor.read_u32()? as i32;
214                let den = cursor.read_u32()? as i32;
215                v.push([num, den]);
216            }
217            TagValue::SRational(v)
218        }
219        TagType::Float => {
220            let mut v = Vec::with_capacity(n);
221            for _ in 0..n {
222                let bits = cursor.read_u32()?;
223                v.push(f32::from_bits(bits));
224            }
225            TagValue::Float(v)
226        }
227        TagType::Double => {
228            let mut v = Vec::with_capacity(n);
229            for _ in 0..n {
230                v.push(cursor.read_f64()?);
231            }
232            TagValue::Double(v)
233        }
234        TagType::Long8 | TagType::Ifd8 => {
235            let mut v = Vec::with_capacity(n);
236            for _ in 0..n {
237                v.push(cursor.read_u64()?);
238            }
239            TagValue::Long8(v)
240        }
241        TagType::SLong8 => {
242            let mut v = Vec::with_capacity(n);
243            for _ in 0..n {
244                v.push(cursor.read_u64()? as i64);
245            }
246            TagValue::SLong8(v)
247        }
248    })
249}