From 3e2877cbbb4a68133ecd963251cd95635d7a0695 Mon Sep 17 00:00:00 2001 From: Marco Arnulfo <103679419+marcoarnulfo@users.noreply.github.com> Date: Mon, 12 Oct 2026 13:19:42 +0200 Subject: [PATCH] fix(forms): a whole look keeps the rest of an indirect /MK (#1322) (#1392) set_props with a complete look read the widget's /MK without resolving it, so an indirect one started from an empty dictionary: a check box lost its Cross mark, its 90-degree rotation and any unknown entry, and the loss survived a save. The /MK is now resolved first, as the check_style branch already does; a direct /MK behaves as before. Adds tests::a_whole_look_keeps_the_rest_of_an_indirect_mk, which fails before this change. --- crates/forms/src/author.rs | 3 ++- crates/forms/src/tests.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/forms/src/author.rs b/crates/forms/src/author.rs index 458bf3f0c4..6d49be1001 100644 --- a/crates/forms/src/author.rs +++ b/crates/forms/src/author.rs @@ -1033,8 +1033,9 @@ pub fn set_props(doc: &mut Document, name: &str, props: &FieldProps) -> Result mk.set(b"BC".to_vec(), arr(c)), None => { diff --git a/crates/forms/src/tests.rs b/crates/forms/src/tests.rs index 45e8501352..975f607c74 100644 --- a/crates/forms/src/tests.rs +++ b/crates/forms/src/tests.rs @@ -1790,6 +1790,37 @@ fn partial_appearance_preserves_each_widgets_indirect_colours_and_border_extensi } } +/// A whole look sets the colours of an indirect /MK and keeps its mark, rotation and unknown +/// entries (#1322), as it already did for a direct one. +#[test] +fn a_whole_look_keeps_the_rest_of_an_indirect_mk() { + let mut doc = fixture(); + let widget = field(&fields(&doc), "agree").widgets[0].obj; + let mut mk = pdfcraft_cos::Dict::new(); + mk.set(b"CA".to_vec(), pdfcraft_cos::PdfString::text("8")); + mk.set(b"R".to_vec(), Object::Int(90)); + mk.set(b"VendorKey".to_vec(), Object::Int(71)); + let mk_ref = doc.add(Object::Dict(mk)); + doc.update_dict(widget, |d| d.set(b"MK".to_vec(), Object::Ref(mk_ref))).unwrap(); + let new = Look { + border: Some([1.0, 0.0, 0.0]), + fill: Some([1.0, 1.0, 0.8]), + width: 2.0, + style: BorderStyle::Solid, + text: [0.0; 3], + font: FieldFont::Helvetica, + }; + set_props(&mut doc, "agree", &FieldProps { look: Some(new), ..FieldProps::default() }).unwrap(); + let doc = reopen(&doc); + let f = field(&fields(&doc), "agree").clone(); + assert_eq!((check_style(&doc, &f), f.widgets[0].rotation), (CheckStyle::Cross, 90)); + let mk = doc.get(widget).as_dict().unwrap().get(b"MK").map(|v| doc.resolve(v)).unwrap(); + let mk = mk.as_dict().unwrap(); + assert_eq!(mk.int(b"VendorKey"), Some(71)); + assert_eq!(look(&doc, &f).border, Some([1.0, 0.0, 0.0])); + assert_eq!(look(&doc, &f).fill, Some([1.0, 1.0, 0.8])); +} + #[test] fn invalid_appearance_patches_are_refused_before_anything_is_written() { let mut doc = fixture();