Skip to main content

tiff/
tags.rs

1use crate::encoder::TiffValue;
2use core::fmt;
3
4macro_rules! tags {
5    {
6        // Permit arbitrary meta items, which include documentation.
7        $( #[$enum_attr:meta] )*
8        $vis:vis enum $name:ident($ty:tt) $(unknown(#[$unknown_meta:meta] $unknown_doc:ident))* {
9            // Each of the `Name = Val,` permitting documentation.
10            $($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
11        }
12    } => {
13        $( #[$enum_attr] )*
14        #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
15        #[non_exhaustive]
16        #[repr($ty)]
17        pub enum $name {
18            $($(#[$ident_attr])* $tag = $val,)*
19            $(
20                #[$unknown_meta]
21                Unknown($ty),
22            )*
23        }
24
25        impl $name {
26            #[inline(always)]
27            const fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
28                match n {
29                    $( $val => Ok($name::$tag), )*
30                    n => Err(n),
31                }
32            }
33
34            #[inline(always)]
35            const fn __to_inner_type(&self) -> $ty {
36                match *self {
37                    $( $name::$tag => $val, )*
38                    $( $name::Unknown($unknown_doc) => { $unknown_doc }, )*
39                }
40            }
41        }
42
43        tags!($name, $ty, $($unknown_doc)*);
44    };
45    // For u16 tags, provide direct inherent primitive conversion methods.
46    ($name:tt, u16, $($unknown_doc:ident)*) => {
47        impl $name {
48            #[inline(always)]
49            pub const fn from_u16(val: u16) -> Option<Self> {
50                match Self::__from_inner_type(val) {
51                    Ok(v) => Some(v),
52                    Err(_) => None,
53                }
54            }
55
56            $(
57            #[inline(always)]
58            pub const fn from_u16_exhaustive($unknown_doc: u16) -> Self {
59                match Self::__from_inner_type($unknown_doc) {
60                    Ok(v) => v,
61                    Err(_) => $name::Unknown($unknown_doc),
62                }
63            }
64            )*
65
66            #[inline(always)]
67            pub const fn to_u16(&self) -> u16 {
68                Self::__to_inner_type(self)
69            }
70        }
71    };
72    // For other tag types, do nothing for now. With concat_idents one could
73    // provide inherent conversion methods for all types.
74    ($name:tt, $ty:tt, $($unknown_doc:literal)*) => {};
75}
76
77// Note: These tags appear in the order they are mentioned in the TIFF reference
78tags! {
79/// TIFF tags
80pub enum Tag(u16) unknown(
81    /// A private or extension tag
82    unknown
83) {
84    // Baseline tags:
85    Artist = 315,
86    // grayscale images PhotometricInterpretation 1 or 3
87    BitsPerSample = 258,
88    CellLength = 265, // TODO add support
89    CellWidth = 264, // TODO add support
90    // palette-color images (PhotometricInterpretation 3)
91    ColorMap = 320, // TODO add support
92    Compression = 259, // TODO add support for 2 and 32773
93    DateTime = 306,
94    ExtraSamples = 338, // TODO add support
95    FillOrder = 266, // TODO add support
96    FreeByteCounts = 289, // TODO add support
97    FreeOffsets = 288, // TODO add support
98    GrayResponseCurve = 291, // TODO add support
99    GrayResponseUnit = 290, // TODO add support
100    HostComputer = 316,
101    ImageDescription = 270,
102    ImageLength = 257,
103    ImageWidth = 256,
104    Make = 271,
105    MaxSampleValue = 281, // TODO add support
106    MinSampleValue = 280, // TODO add support
107    Model = 272,
108    NewSubfileType = 254, // TODO add support
109    Orientation = 274, // TODO add support
110    PhotometricInterpretation = 262,
111    PlanarConfiguration = 284,
112    ResolutionUnit = 296, // TODO add support
113    RowsPerStrip = 278,
114    SamplesPerPixel = 277,
115    Software = 305,
116    StripByteCounts = 279,
117    StripOffsets = 273,
118    SubfileType = 255, // TODO add support
119    Threshholding = 263, // TODO add support
120    XResolution = 282,
121    YResolution = 283,
122    // Advanced tags
123    Predictor = 317,
124    TileWidth = 322,
125    TileLength = 323,
126    TileOffsets = 324,
127    TileByteCounts = 325,
128    SubIfd = 330,
129    // Data Sample Format
130    SampleFormat = 339,
131    SMinSampleValue = 340, // TODO add support
132    SMaxSampleValue = 341, // TODO add support
133    // JPEG
134    JPEGTables = 347,
135    // Subsampling
136    #[doc(alias = "YCbCrSubsampling")]
137    ChromaSubsampling = 530, // TODO add support
138    #[doc(alias = "YCbCrPositioning")]
139    ChromaPositioning = 531, // TODO add support
140    // GeoTIFF
141    ModelPixelScaleTag = 33550, // (SoftDesk)
142    ModelTransformationTag = 34264, // (JPL Carto Group)
143    ModelTiepointTag = 33922, // (Intergraph)
144    // <https://web.archive.org/web/20131111073619/http://www.exif.org/Exif2-1.PDF>
145    // *Do note its typo in the Decimal id*
146    Copyright = 33_432,
147    // <https://web.archive.org/web/20131111073619/http://www.exif.org/Exif2-1.PDF>
148    ExifDirectory = 0x8769,
149    // <https://web.archive.org/web/20131111073619/http://www.exif.org/Exif2-1.PDF>
150    GpsDirectory = 0x8825,
151    // <https://www.color.org/technotes/ICC-Technote-ProfileEmbedding.pdf>
152    IccProfile = 34675,
153    GeoKeyDirectoryTag = 34735, // (SPOT)
154    GeoDoubleParamsTag = 34736, // (SPOT)
155    GeoAsciiParamsTag = 34737, // (SPOT)
156    ExifVersion = 0x9000,
157    GdalNodata = 42113, // Contains areas with missing data
158}
159}
160
161/// Identifies the offset of an IFD.
162///
163/// This is represented as a 64-bit integer but only BigTIFF can utilize the bits. It is encoded
164/// as 32-bit unsigned value ([`Type::LONG`]) in regular TIFF files and as 64-bit unsigned value
165/// ([`Type::IFD8`]) in BigTIFF files.
166#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
167// We could be using `NonZeroU64` here but I find that this complicates semantics. This type
168// represents the integer value stored as a value in a tag. The semantics of treating `0` as an end
169// marker are imposed by the IFD. (It's unclear if Pointer tags such as Exif would allow `0` but in
170// practice it just returns garbage and the validity does not matter greatly to us).
171pub struct IfdPointer(pub u64);
172
173impl fmt::LowerHex for IfdPointer {
174    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175        fmt::LowerHex::fmt(&self.0, f)
176    }
177}
178
179impl core::fmt::UpperHex for IfdPointer {
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        fmt::UpperHex::fmt(&self.0, f)
182    }
183}
184
185tags! {
186/// The type of an IFD entry (a 2 byte field).
187pub enum Type(u16) {
188    /// 8-bit unsigned integer
189    BYTE = 1,
190    /// 8-bit byte that contains a 7-bit ASCII code; the last byte must be zero
191    ASCII = 2,
192    /// 16-bit unsigned integer
193    SHORT = 3,
194    /// 32-bit unsigned integer
195    LONG = 4,
196    /// Fraction stored as two 32-bit unsigned integers
197    RATIONAL = 5,
198    /// 8-bit signed integer
199    SBYTE = 6,
200    /// 8-bit byte that may contain anything, depending on the field
201    UNDEFINED = 7,
202    /// 16-bit signed integer
203    SSHORT = 8,
204    /// 32-bit signed integer
205    SLONG = 9,
206    /// Fraction stored as two 32-bit signed integers
207    SRATIONAL = 10,
208    /// 32-bit IEEE floating point
209    FLOAT = 11,
210    /// 64-bit IEEE floating point
211    DOUBLE = 12,
212    /// 32-bit unsigned integer (offset)
213    IFD = 13,
214    /// BigTIFF 64-bit unsigned integer
215    LONG8 = 16,
216    /// BigTIFF 64-bit signed integer
217    SLONG8 = 17,
218    /// BigTIFF 64-bit unsigned integer (offset)
219    IFD8 = 18,
220}
221}
222
223impl Type {
224    pub(crate) fn byte_len(&self) -> u8 {
225        match *self {
226            Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
227            Type::SHORT | Type::SSHORT => 2,
228            Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
229            Type::LONG8
230            | Type::SLONG8
231            | Type::DOUBLE
232            | Type::RATIONAL
233            | Type::SRATIONAL
234            | Type::IFD8 => 8,
235        }
236    }
237
238    pub(crate) fn value_bytes(&self, count: u64) -> Result<u64, crate::error::TiffError> {
239        let tag_size = u64::from(self.byte_len());
240
241        match count.checked_mul(tag_size) {
242            Some(n) => Ok(n),
243            None => Err(crate::error::TiffError::LimitsExceeded),
244        }
245    }
246
247    pub(crate) fn endian_bytes(self) -> EndianBytes {
248        match self {
249            Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => EndianBytes::One,
250            Type::SHORT | Type::SSHORT => EndianBytes::Two,
251            Type::LONG
252            | Type::SLONG
253            | Type::FLOAT
254            | Type::IFD
255            | Type::RATIONAL
256            | Type::SRATIONAL => EndianBytes::Four,
257            Type::LONG8 | Type::SLONG8 | Type::DOUBLE | Type::IFD8 => EndianBytes::Eight,
258        }
259    }
260}
261
262tags! {
263/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
264/// for reference.
265pub enum CompressionMethod(u16) unknown(
266    /// A custom compression method
267    unknown
268) {
269    None = 1,
270    Huffman = 2,
271    Fax3 = 3,
272    Fax4 = 4,
273    LZW = 5,
274    JPEG = 6,
275    // "Extended JPEG" or "new JPEG" style
276    ModernJPEG = 7,
277    Deflate = 8,
278    OldDeflate = 0x80B2,
279    PackBits = 0x8005,
280
281    // Self-assigned by libtiff
282    ZSTD = 0xC350,
283
284    // Self-assigned by libtiff
285    WebP = 0xC351,
286}
287}
288
289tags! {
290pub enum PhotometricInterpretation(u16) {
291    WhiteIsZero = 0,
292    BlackIsZero = 1,
293    RGB = 2,
294    RGBPalette = 3,
295    TransparencyMask = 4,
296    CMYK = 5,
297    YCbCr = 6,
298    CIELab = 8,
299    IccLab = 9,
300    ItuLab = 10,
301}
302}
303
304tags! {
305pub enum PlanarConfiguration(u16) {
306    Chunky = 1,
307    Planar = 2,
308}
309}
310
311tags! {
312pub enum Predictor(u16) {
313    /// No changes were made to the data
314    None = 1,
315    /// The images' rows were processed to contain the difference of each pixel from the previous one.
316    ///
317    /// This means that instead of having in order `[r1, g1. b1, r2, g2 ...]` you will find
318    /// `[r1, g1, b1, r2-r1, g2-g1, b2-b1, r3-r2, g3-g2, ...]`
319    Horizontal = 2,
320    /// Not currently supported
321    FloatingPoint = 3,
322}
323}
324
325tags! {
326/// Type to represent resolution units
327pub enum ResolutionUnit(u16) {
328    None = 1,
329    Inch = 2,
330    Centimeter = 3,
331}
332}
333
334tags! {
335pub enum SampleFormat(u16) unknown(
336    /// An unknown extension sample format
337    unknown
338) {
339    Uint = 1,
340    Int = 2,
341    IEEEFP = 3,
342    Void = 4,
343}
344}
345
346tags! {
347pub enum ExtraSamples(u16) {
348    /// There is no specified association between the sample and the image.
349    Unspecified = 0,
350    /// The sample is associated alpha, i.e. pre-multiplied color.
351    AssociatedAlpha = 1,
352    /// The sample is unassociated alpha such as a mask. There might be more than one such sample.
353    UnassociatedAlpha = 2,
354}
355}
356
357/// A value represented as in-memory bytes with flexible byteorder.
358pub struct ValueBuffer {
359    /// The raw bytes of the value.
360    bytes: Vec<u8>,
361
362    /// The type of the value.
363    ty: Type,
364
365    /// The number of items, as `bytes` may be oversized while holding bytes that are initialized
366    /// but not used by any value.
367    count: u64,
368
369    /// The byte order of the value.
370    byte_order: ByteOrder,
371}
372
373impl ValueBuffer {
374    /// A value with a count of zero.
375    ///
376    /// The byte order is set to the native byte order of the platform.
377    pub fn empty(ty: Type) -> Self {
378        ValueBuffer {
379            bytes: vec![],
380            ty,
381            count: 0,
382            byte_order: ByteOrder::native(),
383        }
384    }
385
386    /// Create a value with native byte order from in-memory data.
387    pub fn from_value<T: TiffValue>(value: &T) -> Self {
388        ValueBuffer {
389            bytes: value.data().into_owned(),
390            ty: <T as TiffValue>::FIELD_TYPE,
391            count: value.count() as u64,
392            byte_order: ByteOrder::native(),
393        }
394    }
395
396    pub fn byte_order(&self) -> ByteOrder {
397        self.byte_order
398    }
399
400    pub fn data_type(&self) -> Type {
401        self.ty
402    }
403
404    /// The count of items in the value.
405    pub fn count(&self) -> u64 {
406        debug_assert!({
407            self.ty
408                .value_bytes(self.count)
409                .is_ok_and(|n| n <= self.bytes.len() as u64)
410        });
411
412        self.count
413    }
414
415    /// View the underlying raw bytes of this value.
416    pub fn as_bytes(&self) -> &[u8] {
417        &self.bytes[..self.assumed_len_from_count()]
418    }
419
420    /// View the underlying mutable raw bytes of this value.
421    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
422        let len = self.assumed_len_from_count();
423        &mut self.bytes[..len]
424    }
425
426    /// Change the byte order of the value representation.
427    pub fn set_byte_order(&mut self, byte_order: ByteOrder) {
428        let len = self.assumed_len_from_count();
429
430        self.byte_order
431            .convert(self.ty, &mut self.bytes[..len], byte_order);
432
433        self.byte_order = byte_order;
434    }
435
436    /// Prepare the internal for a value `to_len` bytes long.
437    ///
438    /// Shrinks the allocation if it is far too large or extends it if it is too small. In either
439    /// case ensures that at least `to_len` bytes are initialized for [`Self::raw_bytes_mut`].
440    pub(crate) fn prepare_length(&mut self, to_len: usize) {
441        if to_len > self.bytes.len() {
442            self.bytes.resize(to_len, 0);
443        }
444
445        if self.bytes.len() < to_len / 2 {
446            self.bytes.truncate(to_len);
447            self.bytes.shrink_to_fit();
448        }
449    }
450
451    /// Internal method to change the type and count while re-interpreting the byte buffer.
452    ///
453    /// Should only be called after writing bytes to the internal buffer prepared with
454    /// `Self::prepare_length`.
455    pub(crate) fn assume_type(&mut self, ty: Type, count: u64, bo: ByteOrder) {
456        debug_assert!({
457            ty.value_bytes(count)
458                .is_ok_and(|n| n <= self.bytes.len() as u64)
459        });
460
461        self.byte_order = bo;
462        self.ty = ty;
463        self.count = count;
464    }
465
466    pub(crate) fn raw_bytes_mut(&mut self) -> &mut [u8] {
467        &mut self.bytes
468    }
469
470    fn assumed_len_from_count(&self) -> usize {
471        usize::from(self.ty.byte_len()) * self.count as usize
472    }
473}
474
475/// Byte order of the TIFF file.
476#[derive(Clone, Copy, Debug, PartialEq, Eq)]
477pub enum ByteOrder {
478    /// little endian byte order
479    LittleEndian,
480    /// big endian byte order
481    BigEndian,
482}
483
484impl ByteOrder {
485    /// Get the byte order representing the running target.
486    ///
487    /// The infallibility of this method represents the fact that only little and big endian
488    /// systems are supported by the library. No mixed endian and no other weird stuff. (Note: as
489    /// of Rust 1.90 this is a tautology as Rust itself only has those two kinds).
490    pub const fn native() -> Self {
491        match () {
492            #[cfg(target_endian = "little")]
493            () => ByteOrder::LittleEndian,
494            #[cfg(target_endian = "big")]
495            () => ByteOrder::BigEndian,
496            #[cfg(not(any(target_endian = "big", target_endian = "little")))]
497            () => compile_error!("Unsupported target"),
498        }
499    }
500
501    /// Given a typed buffer, convert its contents to the specified byte order in-place.
502    ///
503    /// The buffer is assumed to represent an array of the given type. If the length of the buffer
504    /// is not divisible into an integer number of values, the behavior for the remaining bytes it
505    /// not specified.
506    pub fn convert(self, ty: Type, buffer: &mut [u8], to: ByteOrder) {
507        self.convert_endian_bytes(ty.endian_bytes(), buffer, to)
508    }
509
510    pub(crate) fn convert_endian_bytes(self, cls: EndianBytes, buffer: &mut [u8], to: ByteOrder) {
511        if self == to {
512            return;
513        }
514
515        // FIXME: at MSRV 1.89 or higher use `slice::as_chunks_mut`.
516        match cls {
517            EndianBytes::One => {
518                // No change needed
519            }
520            EndianBytes::Two => {
521                for chunk in buffer.chunks_exact_mut(2) {
522                    let chunk: &mut [u8; 2] = chunk.try_into().unwrap();
523                    *chunk = u16::from_be_bytes(*chunk).to_le_bytes();
524                }
525            }
526            EndianBytes::Four => {
527                for chunk in buffer.chunks_exact_mut(4) {
528                    let chunk: &mut [u8; 4] = chunk.try_into().unwrap();
529                    *chunk = u32::from_be_bytes(*chunk).to_le_bytes();
530                }
531            }
532            EndianBytes::Eight => {
533                for chunk in buffer.chunks_exact_mut(8) {
534                    let chunk: &mut [u8; 8] = chunk.try_into().unwrap();
535                    *chunk = u64::from_be_bytes(*chunk).to_le_bytes();
536                }
537            }
538        }
539    }
540}
541
542/// The size of individual byte-order corrected elements.
543#[derive(Clone, Copy)]
544pub(crate) enum EndianBytes {
545    One,
546    Two,
547    Four,
548    Eight,
549}