From 75b82de0d59778de32f53abd9b48d3a421338335 Mon Sep 17 00:00:00 2001 From: mtlcl410 Date: Thu, 5 Nov 2026 10:14:44 +0800 Subject: [PATCH] M11: PDF/A verify reads an action's type through a reference (6.5.1) An action whose /S refers to /Named or /NOP is checked like one that names it directly, so verification reports it and conversion removes it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn --- crates/preflight/src/lib.rs | 7 ++++--- crates/preflight/src/tests.rs | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/preflight/src/lib.rs b/crates/preflight/src/lib.rs index c615b7cb9f..9e83218a70 100644 --- a/crates/preflight/src/lib.rs +++ b/crates/preflight/src/lib.rs @@ -789,10 +789,11 @@ fn walk_chain(doc: &Document, a: &Dict, read: &mut usize, seen: &mut std::collec } /// The forbidden kind of the action `d` itself, if it is one: a forbidden type, or a Named -/// action for anything but the four of [`NAMED_ACTIONS`], its name given directly or by -/// reference. +/// action for anything but the four of [`NAMED_ACTIONS`]. The type and the name may each be given +/// directly or by reference. fn action_forbidden(doc: &Document, d: &Dict) -> Option { - let s = d.name(b"S")?; + let s = doc.resolve(d.get(b"S")?); + let s = s.as_name()?; if FORBIDDEN_ACTIONS.contains(&s) { return Some(String::from_utf8_lossy(s).into_owned()); } diff --git a/crates/preflight/src/tests.rs b/crates/preflight/src/tests.rs index dc5991d67f..b8f350b19d 100644 --- a/crates/preflight/src/tests.rs +++ b/crates/preflight/src/tests.rs @@ -502,6 +502,8 @@ fn set_state_no_op_and_named_actions_are_checked() { &outline, "<< /Type /Outlines /First 3 0 R /Last 3 0 R /Count 1 >>", "/NextPage", + "/Named", + "/NOP", ], "", ) @@ -520,6 +522,10 @@ fn set_state_no_op_and_named_actions_are_checked() { } // The name given by reference (object 5 is /NextPage). assert!(actions("<< /S /Named /N 5 0 R >>").is_empty()); + // The type given by reference (object 6 is /Named, 7 is /NOP). + assert_eq!(actions("<< /S 6 0 R /N /Print >>"), ["A Named Print action isn't allowed"]); + assert_eq!(actions("<< /S 7 0 R >>"), ["A NOP action isn't allowed"]); + assert!(actions("<< /S 6 0 R /N 5 0 R >>").is_empty()); // Save as PDF/A removes them. for action in ["<< /S /SetState /T [] >>", "<< /S /NOP >>", "<< /S /Named /N /Print >>"] { let mut doc = doc_with(action);