diff --git a/crates/forms/src/lib.rs b/crates/forms/src/lib.rs index 337ae765..514563e5 100644 --- a/crates/forms/src/lib.rs +++ b/crates/forms/src/lib.rs @@ -430,12 +430,14 @@ fn actions_of(doc: &Document, d: &Dict, widgets: &[ObjRef]) -> af::Actions { out } -fn values_of(doc: &Document, o: Option<&Object>) -> Vec { +fn values_of(doc: &Document, o: Option<&Object>, kind: FieldKind) -> Vec { let Some(o) = o else { return Vec::new() }; + // "Off" is a button's off state; in a text or choice field it is the value. + let button = matches!(kind, FieldKind::CheckBox | FieldKind::Radio); match &*doc.resolve(o) { Object::Array(a) => a.iter().filter_map(|x| text_of(&doc.resolve(x))).collect(), Object::Stream(s) => s.decoded().ok().map(|b| PdfString::literal(b).to_text()).into_iter().collect(), - other => text_of(other).filter(|s| s != "Off" && !s.is_empty()).into_iter().collect(), + other => text_of(other).filter(|s| !(button && s == "Off") && !s.is_empty()).into_iter().collect(), } } @@ -818,8 +820,8 @@ fn walk( name: inh.name.clone(), obj: r, kind, - value: values_of(doc, inh.v.as_ref()), - default: values_of(doc, inh.dv.as_ref()), + value: values_of(doc, inh.v.as_ref(), kind), + default: values_of(doc, inh.dv.as_ref(), kind), flags: ff, max_len: inh.max_len, options, diff --git a/crates/forms/src/tests.rs b/crates/forms/src/tests.rs index 975f607c..bee36653 100644 --- a/crates/forms/src/tests.rs +++ b/crates/forms/src/tests.rs @@ -1850,3 +1850,23 @@ fn invalid_appearance_patches_are_refused_before_anything_is_written() { assert!(set_props(&mut doc, "name", &props).is_err()); assert_eq!(write_incremental(&doc, &SaveOptions::default()).unwrap(), before); } + +/// "Off" is a button's off state, not a value; a text or choice field that holds "Off" reads +/// it (#1320), while an unchecked check box still reads as no value. +#[test] +fn a_literal_off_value_reads_as_off_outside_buttons() { + for (kind, value) in [ + (NewField::Text { multiline: false }, FieldValue::Text("Off".into())), + (NewField::Combo { options: vec!["On".into(), "Off".into()], editable: false }, FieldValue::Choice(vec!["Off".into()])), + (NewField::List { options: vec!["On".into(), "Off".into()], multi: false }, FieldValue::Choice(vec!["Off".into()])), + ] { + let mut doc = one_page(); + add_field(&mut doc, 0, [50.0, 600.0, 250.0, 620.0], &kind, Some("power")).unwrap(); + set_value(&mut doc, "power", &value).unwrap(); + assert_eq!(field(&fields(&reopen(&doc)), "power").value, ["Off"], "{kind:?}"); + } + let mut doc = one_page(); + add_field(&mut doc, 0, [50.0, 600.0, 70.0, 620.0], &NewField::CheckBox, Some("power")).unwrap(); + set_value(&mut doc, "power", &FieldValue::Check(false)).unwrap(); + assert!(field(&fields(&doc), "power").value.is_empty()); +}