diff --git a/src/sections/image_resources_section.rs b/src/sections/image_resources_section.rs index f740008..308b141 100644 --- a/src/sections/image_resources_section.rs +++ b/src/sections/image_resources_section.rs @@ -1,3 +1,4 @@ +use core::str::Utf8Error; use std::collections::HashMap; use std::ops::Range; @@ -10,6 +11,7 @@ use crate::sections::PsdCursor; const EXPECTED_RESOURCE_BLOCK_SIGNATURE: [u8; 4] = [56, 66, 73, 77]; const EXPECTED_DESCRIPTOR_VERSION: u32 = 16; const RESOURCE_SLICES_INFO: i16 = 1050; +const RESOURCE_XMP_INFO: i16 = 1060; mod image_resource; @@ -33,6 +35,9 @@ pub enum ImageResourcesSectionError { )] InvalidSignature {}, + #[error("Invalid utf8 in xmp definition: {0}")] + InvalidXmpText(Utf8Error), + #[error("Invalid resource descriptor: {0}")] InvalidResource(ImageResourcesDescriptorError), } @@ -57,6 +62,11 @@ impl ImageResourcesSection { .map_err(ImageResourcesSectionError::InvalidResource)?; resources.push(ImageResource::Slices(slices_image_resource)); } + _ if rid == RESOURCE_XMP_INFO => { + let xml = std::str::from_utf8(&cursor.get_ref()[block.data_range]) + .map_err(ImageResourcesSectionError::InvalidXmpText)?; + resources.push(ImageResource::Xmp(xml.to_string())); + } _ => {} } } diff --git a/src/sections/image_resources_section/image_resource.rs b/src/sections/image_resources_section/image_resource.rs index 6611d3d..24758ac 100644 --- a/src/sections/image_resources_section/image_resource.rs +++ b/src/sections/image_resources_section/image_resource.rs @@ -5,6 +5,7 @@ use crate::sections::image_resources_section::DescriptorStructure; #[allow(missing_docs)] pub enum ImageResource { Slices(SlicesImageResource), + Xmp(String), } /// Comes from a slices resource block diff --git a/tests/image_resources_section.rs b/tests/image_resources_section.rs index 77d7f6d..2a2a78b 100644 --- a/tests/image_resources_section.rs +++ b/tests/image_resources_section.rs @@ -10,8 +10,9 @@ fn image_check_1x1p_bound_field() { let psd = Psd::from_bytes(psd).unwrap(); - let descriptors = match &psd.resources()[0] { + let descriptors = match &psd.resources()[1] { ImageResource::Slices(s) => s.descriptors(), + ImageResource::Xmp(..) => panic!("unexpected resource ordering"), }; let descriptor = descriptors.get(0).unwrap(); let bounds = descriptor.fields.get("bounds").unwrap(); @@ -41,8 +42,9 @@ fn image_check_16x16p_bound_field() { let psd = Psd::from_bytes(psd).unwrap(); - let descriptors = match &psd.resources()[0] { + let descriptors = match &psd.resources()[1] { ImageResource::Slices(s) => s.descriptors(), + ImageResource::Xmp(..) => panic!("unexpected resource ordering"), }; let descriptor = descriptors.get(0).unwrap(); let bounds = descriptor.fields.get("bounds").unwrap(); @@ -83,3 +85,178 @@ fn image_odd_length_pascal_string() { assert!(psd.layers().is_empty()); } + +/// Check that the XMP data is parsed correctly from one of the fixtures. +/// +/// cargo test --test image_resources_section xmp_check_parsed_as_string -- --exact +#[test] +fn xmp_check_parsed_as_string() { + let psd = include_bytes!("./fixtures/two-layers-red-green-1x1.psd"); + + let psd = Psd::from_bytes(psd).unwrap(); + + let xmp_string = match &psd.resources()[0] { + ImageResource::Xmp(s) => s, + ImageResource::Slices(..) => panic!("unexpected resource ordering"), + }; + assert_eq!(xmp_string, " + + + + Adobe Photoshop CC 2018 (Macintosh) + 2019-02-18T21:23:30-05:00 + 2019-02-18T21:23:41-05:00 + 2019-02-18T21:23:41-05:00 + application/vnd.adobe.photoshop + xmp.iid:f88d4ee4-9872-4982-a8b8-a0ed4e5deb99 + xmp.did:031c6a78-d72a-48c5-926c-bcc201d2b60b + xmp.did:031c6a78-d72a-48c5-926c-bcc201d2b60b + + + + created + xmp.iid:031c6a78-d72a-48c5-926c-bcc201d2b60b + 2019-02-18T21:23:30-05:00 + Adobe Photoshop CC 2018 (Macintosh) + + + saved + xmp.iid:f88d4ee4-9872-4982-a8b8-a0ed4e5deb99 + 2019-02-18T21:23:41-05:00 + Adobe Photoshop CC 2018 (Macintosh) + / + + + + 3 + Display + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); +} diff --git a/tests/slices_resource.rs b/tests/slices_resource.rs index 1c4e7bb..4bbae10 100644 --- a/tests/slices_resource.rs +++ b/tests/slices_resource.rs @@ -26,10 +26,11 @@ fn name_of_slices_resource_group() { let psd = std::fs::read(&file).unwrap(); let psd = Psd::from_bytes(&psd).unwrap(); - match &psd.resources()[0] { + match &psd.resources()[1] { ImageResource::Slices(slices) => { assert_eq!(slices.name().as_str(), expected_slices_name); } + ImageResource::Xmp(..) => panic!("unexpected resource ordering"), }; } } @@ -44,14 +45,12 @@ fn slices_v7_8() -> Result<()> { let psd = include_bytes!("./fixtures/slices-v8.psd"); let psd = Psd::from_bytes(psd)?; - match &psd.resources()[0] { - ImageResource::Slices(slices) => { - assert_eq!(slices.name().as_str(), "\u{0}"); + let descriptors = match &psd.resources()[1] { + ImageResource::Slices(s) => { + assert_eq!(s.name().as_str(), "\u{0}"); + s.descriptors() } - }; - - let descriptors = match &psd.resources()[0] { - ImageResource::Slices(s) => s.descriptors(), + ImageResource::Xmp(..) => panic!("unexpected resource ordering"), }; let descriptor = descriptors.get(0).unwrap(); let bounds = descriptor.fields.get("bounds").unwrap();