mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 12:32:52 +00:00
M11: PDF/A verify allows only XMP's value forms: one rdf:value, parseType Resource, empty rdf:resource (6.6.2.3.1)
A qualified value has exactly one rdf:value; a parse type other than Resource, which XMP doesn't use (ISO 16684-1 7.9.2.5), breaks the value; and an rdf:resource element holds no element content in any form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
This commit is contained in:
1 parent
7bce37fefc
commit
abf0f94e78
2 files changed
+30
-4
No files matched your search
@@ -708,3 +708,21 @@ fn xmp_value_types_are_reported_by_verify() {
|
||||
);
|
||||
assert_eq!(issues(&doc).len(), 1);
|
||||
}
|
||||
|
||||
/// PDF/A-2 6.6.2.3.1, the RDF/XML value forms XMP allows (ISO 16684-1 7.9.2.5): one `rdf:value`
|
||||
/// in a qualified value, no parse type but `Resource`, and an `rdf:resource` element left empty.
|
||||
#[test]
|
||||
fn xmp_value_forms_are_those_xmp_allows() {
|
||||
// A qualified value has exactly one rdf:value.
|
||||
assert_eq!(
|
||||
mistyped(r#"><crs:Contrast rdf:parseType="Resource"><rdf:value>5</rdf:value><rdf:value>five</rdf:value></crs:Contrast>"#),
|
||||
["crs:Contrast"]
|
||||
);
|
||||
// Only parseType="Resource" has a place in XMP; an rdf:resource element holds no element.
|
||||
assert_eq!(mistyped(r#"><crs:Contrast rdf:parseType="Literal">5</crs:Contrast>"#), ["crs:Contrast"]);
|
||||
assert_eq!(mistyped(r#"><xmpMM:DerivedFrom rdf:parseType="Literal" stRef:documentID="d"/>"#), ["xmpMM:DerivedFrom"]);
|
||||
assert_eq!(
|
||||
mistyped(r#"><xmpMM:DerivedFrom rdf:resource="urn:example"><rdf:Description stRef:documentID="d"/></xmpMM:DerivedFrom>"#),
|
||||
["xmpMM:DerivedFrom"]
|
||||
);
|
||||
}
|
||||
@@ -535,6 +535,11 @@ fn fits(ty: Ty, e: &Node<'_, '_>, depth: usize) -> bool {
|
||||
let children: Vec<Node<'_, '_>> = e.children().filter(Node::is_element).collect();
|
||||
// A qualified value: `rdf:value` holds the value itself (ISO 16684-1 7.9.2.5).
|
||||
let resource = e.attribute((RDF, "parseType")) == Some("Resource");
|
||||
// `parseType="Resource"` is the only parse type XMP has (ISO 16684-1 7.9.2.5); an
|
||||
// `rdf:resource` element is empty.
|
||||
if e.attribute((RDF, "parseType")).is_some_and(|p| p != "Resource") || e.attribute((RDF, "resource")).is_some() && !children.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let description = match children.as_slice() {
|
||||
[d] if is(d, RDF, "Description") => Some(*d),
|
||||
_ => None,
|
||||
@@ -543,10 +548,13 @@ fn fits(ty: Ty, e: &Node<'_, '_>, depth: usize) -> bool {
|
||||
// attribute of the description or of an empty property element. Its qualifiers that are
|
||||
// predefined properties keep their types too.
|
||||
let node = if resource { Some(*e) } else { description };
|
||||
if let Some(n) = node
|
||||
&& let Some(v) = n.children().find(|c| is(c, RDF, "value"))
|
||||
{
|
||||
return blank(e) && blank(&n) && fits(ty, &v, deeper) && predefined_ok(&n, deeper);
|
||||
if let Some(n) = node {
|
||||
// A qualified value has one `rdf:value`.
|
||||
match n.children().filter(|c| is(c, RDF, "value")).collect::<Vec<_>>().as_slice() {
|
||||
[] => {}
|
||||
[v] => return blank(e) && blank(&n) && fits(ty, v, deeper) && predefined_ok(&n, deeper),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
let attribute_node = description.or_else(|| (children.is_empty() && !resource).then_some(*e));
|
||||
if let Some(n) = attribute_node
|
||||
|
||||
Reference in new issue
Block a user