Skip to content

Commit

Permalink
Add XmpMeta::from_str_requiring_xmp_meta (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
scouten-adobe authored Nov 2, 2022
1 parent 25e564c commit 2f5aec5
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 5 deletions.
5 changes: 3 additions & 2 deletions src/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,14 @@ extern "C" {

CXmpMeta* CXmpMetaParseFromBuffer(CXmpError* outError,
const char* buffer,
AdobeXMPCommon::uint32 buffer_size) {
AdobeXMPCommon::uint32 buffer_size,
AdobeXMPCommon::uint32 options) {
#ifndef NOOP_FFI
init_xmp();
CXmpMeta* result = new CXmpMeta;

try {
result->m.ParseFromBuffer(buffer, buffer_size);
result->m.ParseFromBuffer(buffer, buffer_size, options);
return result;
}
catch (XMP_Error& e) {
Expand Down
1 change: 1 addition & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ extern "C" {
out_error: *mut CXmpError,
buffer: *const u8,
buffer_size: u32,
options: u32,
) -> *mut CXmpMeta;

pub(crate) fn CXmpMetaSerializeToBuffer(
Expand Down
178 changes: 178 additions & 0 deletions src/tests/xmp_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,184 @@ mod from_str {
}
}

mod from_str_requiring_xmp_meta {
use crate::{tests::fixtures::*, XmpMeta, XmpValue};

const NO_META: &str = r#"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:xap="http://ns.adobe.com/xap/1.0/"
xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
xmlns:exif="http://ns.adobe.com/exif/1.0/"
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/"
xmlns:xapRights="http://ns.adobe.com/xap/1.0/rights/"
dc:format="application/vnd.adobe.photoshop"
xap:CreatorTool="Adobe Photoshop CS2 Windows"
xap:CreateDate="2006-04-25T15:32:01+02:00"
xap:ModifyDate="2006-04-27T15:38:36.655+02:00"
xap:MetadataDate="2006-04-26T16:47:10+02:00"
xapMM:DocumentID="uuid:FE607D9B5FD4DA118B7787757E22306B"
xapMM:InstanceID="uuid:BF664E7B33D5DA119129F691B53239AD"
tiff:Orientation="1"
tiff:XResolution="720000/10000"
tiff:YResolution="720000/10000"
tiff:ResolutionUnit="2"
tiff:NativeDigest="256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;6F0EC2A1D6ADFA4DF4BB00D7C83AFAC0"
exif:PixelXDimension="200"
exif:PixelYDimension="200"
exif:ColorSpace="-1"
exif:NativeDigest="36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;D891A8B493E755131A3267739F6277DB"
photoshop:ColorMode="3"
photoshop:ICCProfile="Dell 1905FP Color Profile"
photoshop:CaptionWriter="Stefan"
photoshop:History=""
pdf:Keywords="&quot;XMP metadata schema XML RDF&quot;"
pdf:Copyright="2005 Adobe Systems Inc."
pdfx:Copyright="2005 Adobe Systems Inc."
xapRights:Marked="False">
<dc:description>
<rdf:Alt>
<rdf:li xml:lang="x-default">a test file (öäüßÖÄÜ€中文)</rdf:li>
</rdf:Alt>
</dc:description>
<dc:title>
<rdf:Alt>
<rdf:li xml:lang="x-default">Purple Square</rdf:li>
</rdf:Alt>
</dc:title>
<dc:creator>
<rdf:Seq>
<rdf:li>Llywelyn</rdf:li>
</rdf:Seq>
</dc:creator>
<dc:subject>
<rdf:Bag>
<rdf:li>purple</rdf:li>
<rdf:li>square</rdf:li>
<rdf:li>Stefan</rdf:li>
<rdf:li>XMP</rdf:li>
<rdf:li>XMPFiles</rdf:li>
<rdf:li>test</rdf:li>
</rdf:Bag>
</dc:subject>
</rdf:Description>
</rdf:RDF>
"#;

#[test]
fn happy_path() {
let m = XmpMeta::from_str_requiring_xmp_meta(PURPLE_SQUARE_XMP, false).unwrap();

assert_eq!(
m.property("http://ns.adobe.com/xap/1.0/", "CreatorTool")
.unwrap(),
XmpValue {
value: "Adobe Photoshop CS2 Windows".to_owned(),
options: 0
}
);

assert_eq!(
m.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfile")
.unwrap(),
XmpValue {
value: "Dell 1905FP Color Profile".to_owned(),
options: 0
}
);

assert!(m
.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfilx")
.is_none());
}

#[test]
fn missing_xmp_meta_required() {
// TODO (https://github.com/adobe/xmp-toolkit-rs/issues/135):
// I think this should be an error response, not a silent
// Ok(default) response.
assert!(XmpMeta::from_str_requiring_xmp_meta(NO_META, true).is_ok());

// Should be:
// XmpError {
// error_type: XmpErrorType::BadSerialize,
// debug_message: "x".to_owned()
// }
}

#[test]
fn missing_xmp_meta_not_required() {
let m = XmpMeta::from_str_requiring_xmp_meta(NO_META, false).unwrap();

println!("m = {:#?}", m);

assert_eq!(
m.property("http://ns.adobe.com/xap/1.0/", "CreatorTool")
.unwrap(),
XmpValue {
value: "Adobe Photoshop CS2 Windows".to_owned(),
options: 0
}
);

assert_eq!(
m.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfile")
.unwrap(),
XmpValue {
value: "Dell 1905FP Color Profile".to_owned(),
options: 0
}
);

assert!(m
.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfilx")
.is_none());
}

#[test]
fn bad_xmp() {
// TXMPMeta::ParseFromBuffer doesn't seem to throw exceptions,
// regardless of how badly-formed the XMP is. This test merely
// confirms that we pass that behavior through.
let m = XmpMeta::from_str_requiring_xmp_meta("this is not XMP", false).unwrap();

assert!(m
.property("http://ns.adobe.com/xap/1.0/", "CreatorTool")
.is_none());

assert!(m
.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfile")
.is_none());

assert!(m
.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfilx")
.is_none());
}

#[test]
fn empty_string() {
// TXMPMeta::ParseFromBuffer doesn't seem to throw exceptions,
// regardless of how badly-formed the XMP is. This test merely
// confirms that we pass that behavior through.
let m = XmpMeta::from_str_requiring_xmp_meta("", false).unwrap();

assert!(m
.property("http://ns.adobe.com/xap/1.0/", "CreatorTool")
.is_none());

assert!(m
.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfile")
.is_none());

assert!(m
.property("http://ns.adobe.com/photoshop/1.0/", "ICCProfilx")
.is_none());
}
}

mod to_string_with_options {
use std::str::FromStr;

Expand Down
31 changes: 28 additions & 3 deletions src/xmp_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,30 @@ impl XmpMeta {
}
}

/// Creates a new `XmpMeta` struct and populates it with metadata from a
/// string containing serialized RDF. This string must be a complete RDF
/// parse stream.
///
/// ## Arguments
///
/// * `s`: XMP string to be read
/// * `require_xmp_meta`: if `true`, the `x:xmpmeta` XML element is required
/// around `rdf:RDF`
pub fn from_str_requiring_xmp_meta(s: &str, require_xmp_meta: bool) -> XmpResult<Self> {
let mut err = ffi::CXmpError::default();
let bytes = s.as_bytes();

let options = if require_xmp_meta { 1 } else { 0 };

let m = unsafe {
ffi::CXmpMetaParseFromBuffer(&mut err, bytes.as_ptr(), bytes.len() as u32, options)
};

XmpError::raise_from_c(&err)?;

Ok(XmpMeta { m: Some(m) })
}

/// Converts metadata in this XMP object into a string as RDF.
///
/// This struct also implements [`std::fmt::Display`] which will provide
Expand Down Expand Up @@ -1781,12 +1805,13 @@ impl FromStr for XmpMeta {
///
/// ## Arguments
///
/// * `xmp`: XMP string to be read
/// * `s`: XMP string to be read
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut err = ffi::CXmpError::default();
let bytes = s.as_bytes();
let m =
unsafe { ffi::CXmpMetaParseFromBuffer(&mut err, bytes.as_ptr(), bytes.len() as u32) };
let m = unsafe {
ffi::CXmpMetaParseFromBuffer(&mut err, bytes.as_ptr(), bytes.len() as u32, 0)
};
XmpError::raise_from_c(&err)?;

Ok(XmpMeta { m: Some(m) })
Expand Down

0 comments on commit 2f5aec5

Please sign in to comment.