Skip to content

Commit

Permalink
Parse XMP metadata as String
Browse files Browse the repository at this point in the history
  • Loading branch information
willstott101 committed Aug 25, 2023
1 parent 5276ab7 commit 254970d
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 12 deletions.
10 changes: 10 additions & 0 deletions src/sections/image_resources_section.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::str::Utf8Error;
use std::collections::HashMap;
use std::ops::Range;

Expand All @@ -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;

Expand All @@ -33,6 +35,9 @@ pub enum ImageResourcesSectionError {
)]
InvalidSignature {},

#[error("Invalid utf8 in xmp definition: {0}")]
InvalidXmpText(Utf8Error),

#[error("Invalid resource descriptor: {0}")]
InvalidResource(ImageResourcesDescriptorError),
}
Expand All @@ -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()));
}
_ => {}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/sections/image_resources_section/image_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
181 changes: 179 additions & 2 deletions tests/image_resources_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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, "<?xpacket begin=\"\u{feff}\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>
<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 5.6-c140 79.160451, 2017/05/06-01:08:21 \">
<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">
<rdf:Description rdf:about=\"\"
xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"
xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\"
xmlns:stEvt=\"http://ns.adobe.com/xap/1.0/sType/ResourceEvent#\"
xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\">
<xmp:CreatorTool>Adobe Photoshop CC 2018 (Macintosh)</xmp:CreatorTool>
<xmp:CreateDate>2019-02-18T21:23:30-05:00</xmp:CreateDate>
<xmp:MetadataDate>2019-02-18T21:23:41-05:00</xmp:MetadataDate>
<xmp:ModifyDate>2019-02-18T21:23:41-05:00</xmp:ModifyDate>
<dc:format>application/vnd.adobe.photoshop</dc:format>
<xmpMM:InstanceID>xmp.iid:f88d4ee4-9872-4982-a8b8-a0ed4e5deb99</xmpMM:InstanceID>
<xmpMM:DocumentID>xmp.did:031c6a78-d72a-48c5-926c-bcc201d2b60b</xmpMM:DocumentID>
<xmpMM:OriginalDocumentID>xmp.did:031c6a78-d72a-48c5-926c-bcc201d2b60b</xmpMM:OriginalDocumentID>
<xmpMM:History>
<rdf:Seq>
<rdf:li rdf:parseType=\"Resource\">
<stEvt:action>created</stEvt:action>
<stEvt:instanceID>xmp.iid:031c6a78-d72a-48c5-926c-bcc201d2b60b</stEvt:instanceID>
<stEvt:when>2019-02-18T21:23:30-05:00</stEvt:when>
<stEvt:softwareAgent>Adobe Photoshop CC 2018 (Macintosh)</stEvt:softwareAgent>
</rdf:li>
<rdf:li rdf:parseType=\"Resource\">
<stEvt:action>saved</stEvt:action>
<stEvt:instanceID>xmp.iid:f88d4ee4-9872-4982-a8b8-a0ed4e5deb99</stEvt:instanceID>
<stEvt:when>2019-02-18T21:23:41-05:00</stEvt:when>
<stEvt:softwareAgent>Adobe Photoshop CC 2018 (Macintosh)</stEvt:softwareAgent>
<stEvt:changed>/</stEvt:changed>
</rdf:li>
</rdf:Seq>
</xmpMM:History>
<photoshop:ColorMode>3</photoshop:ColorMode>
<photoshop:ICCProfile>Display</photoshop:ICCProfile>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end=\"w\"?>");
}
19 changes: 9 additions & 10 deletions tests/slices_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
};
}
}
Expand All @@ -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()[0] {
ImageResource::Slices(s) => s.descriptors(),
let descriptors = match &psd.resources()[1] {
ImageResource::Slices(s) => {
assert_eq!(s.name().as_str(), "\u{0}");
s.descriptors()
},
ImageResource::Xmp(..) => panic!("unexpected resource ordering"),
};
let descriptor = descriptors.get(0).unwrap();
let bounds = descriptor.fields.get("bounds").unwrap();
Expand Down

0 comments on commit 254970d

Please sign in to comment.