mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-26 19:19:10 -06:00
The field reader dropped the value "Off" for every field kind, so a text field, combo box or single-select list set to "Off" read as empty and exported an empty value to CSV, text, XML, XFDF and FDF, although the document stored it. "Off" is now treated as no value only for check boxes and radio buttons, where it is the off state. Adds tests::a_literal_off_value_reads_as_off_outside_buttons, which fails before this change.
This commit is contained in:
@@ -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<String> {
|
||||
fn values_of(doc: &Document, o: Option<&Object>, kind: FieldKind) -> Vec<String> {
|
||||
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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user