From ff1d31533b4bbff5f13d7725961e7c9b0dfdf70d Mon Sep 17 00:00:00 2001 From: Jim Rybarski Date: Fri, 17 Aug 2018 11:43:51 -0500 Subject: [PATCH] Add support for ASCII tags --- src/decoder/ifd.rs | 28 +++++++++++++++++----------- src/decoder/mod.rs | 11 +++++++++++ src/error.rs | 7 +++++++ tests/decode_images.rs | 20 +++++++++++++++++++- 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index b349e64..4ffc22f 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -6,7 +6,7 @@ use std::collections::{HashMap}; use super::stream::{ByteOrder, SmartReader, EndianReader}; use ::{TiffError, TiffResult}; -use self::Value::{Unsigned, List, Rational}; +use self::Value::{Unsigned, List, Rational, Ascii}; macro_rules! tags { {$( @@ -33,7 +33,7 @@ macro_rules! tags { // Note: These tags appear in the order they are mentioned in the TIFF reference tags!{ // Baseline tags: - Artist 315; // TODO add support + Artist 315; // grayscale images PhotometricInterpretation 1 or 3 BitsPerSample 258; CellLength 265; // TODO add support @@ -41,22 +41,22 @@ tags!{ // palette-color images (PhotometricInterpretation 3) ColorMap 320; // TODO add support Compression 259; // TODO add support for 2 and 32773 - Copyright 33_432; // TODO add support - DateTime 306; // TODO add support + Copyright 33_432; + DateTime 306; ExtraSamples 338; // TODO add support FillOrder 266; // TODO add support FreeByteCounts 289; // TODO add support FreeOffsets 288; // TODO add support GrayResponseCurve 291; // TODO add support GrayResponseUnit 290; // TODO add support - HostComputer 316; // TODO add support - ImageDescription 270; // TODO add support + HostComputer 316; + ImageDescription 270; ImageLength 257; ImageWidth 256; - Make 271; // TODO add support + Make 271; MaxSampleValue 281; // TODO add support MinSampleValue 280; // TODO add support - Model 272; // TODO add support + Model 272; NewSubfileType 254; // TODO add support Orientation 274; // TODO add support PhotometricInterpretation 262; @@ -91,7 +91,8 @@ pub enum Value { //Signed(i32), Unsigned(u32), List(Vec), - Rational(u32, u32) + Rational(u32, u32), + Ascii(String) } impl Value { @@ -113,8 +114,8 @@ impl Value { Ok(new_vec) }, Unsigned(val) => Ok(vec![val]), - Rational(numerator, denominator) => Ok(vec![numerator, denominator]) - //_ => Err(::image::FormatError("Tag data malformed.".to_string())) + Rational(numerator, denominator) => Ok(vec![numerator, denominator]), + Ascii(val) => Ok(val.chars().map(|x| x as u32).collect()) } } } @@ -196,6 +197,11 @@ impl Entry { } Ok(List(v)) }, + (Type::ASCII, n) => { + try!(decoder.goto_offset(try!(self.r(bo).read_u32()))); + let string = try!(decoder.read_string(n as usize)); + Ok(Ascii(string)) + } _ => Err(TiffError::UnsupportedError("Unsupported data type.".to_string())) } } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 045944a..e4305d7 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -2,6 +2,7 @@ use std::io::{self, Read, Seek}; use std::mem; use num_traits::{FromPrimitive, Num}; use std::collections::HashMap; +use std::string::FromUtf8Error; use ::{ColorType, TiffError, TiffResult}; @@ -279,6 +280,16 @@ impl Decoder { self.reader.read_u32() } + /// Reads a string + #[inline] + pub fn read_string(&mut self, length: usize) -> Result { + let mut out = String::with_capacity(length); + self.reader.read_to_string(&mut out); + // Strings may be null-terminated, so we trim anything downstream of the null byte + let trimmed = out.bytes().take_while(|&n| n != 0).collect::>(); + String::from_utf8(trimmed) + } + /// Reads a TIFF IFA offset/value field #[inline] pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> { diff --git a/src/error.rs b/src/error.rs index bc6bf70..3e72932 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ use std::error::Error; use std::fmt; use std::io; +use std::string; /// Tiff error kinds. #[derive(Debug)] @@ -49,5 +50,11 @@ impl From for TiffError { } } +impl From for TiffError { + fn from(err: string::FromUtf8Error) -> TiffError { + TiffError::FormatError(String::from("Image contains invalid tag.")) + } +} + /// Result of an image decoding/encoding process pub type TiffResult = Result; diff --git a/tests/decode_images.rs b/tests/decode_images.rs index f8e3a25..5881733 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -1,7 +1,7 @@ extern crate tiff; use tiff::ColorType; -use tiff::decoder::{Decoder, DecodingResult}; +use tiff::decoder::{Decoder, DecodingResult, ifd::Tag, ifd::Value}; use std::fs::File; @@ -45,6 +45,24 @@ fn test_rgb_u16() assert!(img_res.is_ok()); } +#[test] +fn test_string_tags() +{ + // these files have null-terminated strings for their Software tag. One has extra bytes after + // the null byte, so we check both to ensure that we're truncating properly + let filenames = vec!["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"]; + for filename in filenames.iter() { + let path = format!("./tests/images/{}", filename); + let img_file = File::open(path).expect("can't open file"); + let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); + let software= decoder.get_tag(Tag::Software).unwrap(); + match software { + Value::Ascii(s) => assert_eq!(s, String::from("GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/")), + _ => assert!(false) + }; + } +} + // TODO: GrayA support //#[test] //fn test_gray_alpha_u8()