From 5cb98a8f1bbc28af5500a6ac1ff4c85b33a9b2c9 Mon Sep 17 00:00:00 2001 From: KAMADA Ken'ichi Date: Mon, 12 Dec 2016 21:27:35 +0900 Subject: [PATCH] Allow Exif and other IFDs in the thumbnail image. The Exif specfication allows Exif/GPS/Interoperability IFDs in the thumbnail image (1st IFD) as well as in the primary one (0th IFD). Do not define separate sets of tag constants for primary and thumbnail images because it doubles the number of constants. Instead, add a flag in struct Field to distinguish them. --- src/tag.rs | 19 ++++++++----------- src/tiff.rs | 23 +++++++++++++++-------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/tag.rs b/src/tag.rs index 005fcf5..5cd35fc 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -89,15 +89,13 @@ impl fmt::Display for Tag { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Context { /// TIFF attributes defined in the TIFF Rev. 6.0 specification. - Tiff, // 0th IFD + Tiff, // 0th/1st IFD /// Exif attributes. - Exif, // 0th IFD -- Exif IFD + Exif, // 0th/1st IFD -- Exif IFD /// GPS attributes. - Gps, // 0th IFD -- GPS IFD + Gps, // 0th/1st IFD -- GPS IFD /// Interoperability attributes. - Interop, // 0th IFD -- Exif IFD -- Interoperability IFD - /// TIFF fields in the 1st IFD, which represents the thumbnail image. - Thumb, // 1st IFD + Interop, // 0th/1st IFD -- Exif IFD -- Interoperability IFD } struct TagInfo { @@ -149,7 +147,8 @@ generate_well_known_tag_constants!( /// structure of Exif data and will not be returned to the user. (InteropIFDPointer, 0xa005, "Interoperability IFD pointer"), - // TIFF attributes [EXIF23 4.6.4 Table 4 and 4.6.8 Table 17]. + // TIFF primary and thumbnail attributes [EXIF23 4.6.4 Table 4, + // 4.6.8 Table 17, and 4.6.8 Table 21]. |Context::Tiff| (ImageWidth, 0x100, "Image width"), @@ -175,16 +174,14 @@ generate_well_known_tag_constants!( (Artist, 0x13b, "Person who created the image"), (WhitePoint, 0x13e, "White point chromaticity"), (PrimaryChromaticities, 0x13f, "Chromaticities of primaries"), - // (JPEGInterchangeFormat, 0x201, "Offset to JPEG SOI"), - // (JPEGInterchangeFormatLength, 0x202, "Bytes of JPEG data"), + (JPEGInterchangeFormat, 0x201, "Offset to JPEG SOI"), + (JPEGInterchangeFormatLength, 0x202, "Bytes of JPEG data"), (YCbCrCoefficients, 0x211, "Color space transformation matrix coefficients"), (YCbCrSubSampling, 0x212, "Subsampling ratio of Y to C"), (YCbCrPositioning, 0x213, "Y and C positioning"), (ReferenceBlackWhite, 0x214, "Pair of black and white reference values"), (Copyright, 0x8298, "Copyright holder"), - // Thumbnail Tiff attributes [EXIF23 4.6.4 Table 4 and 4.6.8 Table 21]. - // Exif IFD attributes [EXIF23 4.6.5 Table 7 and 4.6.8 Table 18]. |Context::Exif| diff --git a/src/tiff.rs b/src/tiff.rs index eb027f6..c40ae24 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -39,7 +39,11 @@ const TIFF_FORTY_TWO: u16 = 0x002a; /// A TIFF field. #[derive(Debug)] pub struct Field<'a> { + /// The tag of this field. pub tag: Tag, + /// False for the primary image and true for the thumbnail. + pub thumbnail: bool, + /// The value of this field. pub value: Value<'a>, } @@ -63,11 +67,11 @@ fn parse_exif_sub(data: &[u8]) return Err(Error::InvalidFormat("Invalid forty two")); } let ifd_offset = E::loadu32(data, 4) as usize; - parse_ifd::(data, ifd_offset, Context::Tiff) + parse_ifd::(data, ifd_offset, Context::Tiff, false) } // Parse IFD [EXIF23 4.6.2]. -fn parse_ifd(data: &[u8], offset: usize, ctx: Context) +fn parse_ifd(data: &[u8], offset: usize, ctx: Context, thumbnail: bool) -> Result, Error> where E: Endian { // Count (the number of the entries). if data.len() < offset || data.len() - offset < 2 { @@ -108,16 +112,19 @@ fn parse_ifd(data: &[u8], offset: usize, ctx: Context) // element of the field. let tag = Tag(ctx, tag); if tag == tag::ExifIFDPointer { - let mut v = try!(parse_ifd::(data, ofs, Context::Exif)); + let mut v = try!( + parse_ifd::(data, ofs, Context::Exif, thumbnail)); fields.append(&mut v); } else if tag == tag::GPSInfoIFDPointer { - let mut v = try!(parse_ifd::(data, ofs, Context::Gps)); + let mut v = try!( + parse_ifd::(data, ofs, Context::Gps, thumbnail)); fields.append(&mut v); } else if tag == tag::InteropIFDPointer { - let mut v = try!(parse_ifd::(data, ofs, Context::Interop)); + let mut v = try!( + parse_ifd::(data, ofs, Context::Interop, thumbnail)); fields.append(&mut v); } else { - fields.push(Field { tag: tag, value: val }); + fields.push(Field { tag: tag, thumbnail: thumbnail, value: val }); } } @@ -127,11 +134,11 @@ fn parse_ifd(data: &[u8], offset: usize, ctx: Context) } let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12) as usize; if next_ifd_offset != 0 { - if ctx != Context::Tiff { + if ctx != Context::Tiff || thumbnail { return Err(Error::InvalidFormat("Unexpected next IFD")); } let mut v = try!( - parse_ifd::(data, next_ifd_offset, Context::Thumb)); + parse_ifd::(data, next_ifd_offset, Context::Tiff, true)); fields.append(&mut v); }