Skip to main content

apply_choice_multi

Function apply_choice_multi 

Source
pub fn apply_choice_multi(
    doc: &mut Document,
    name: &str,
    values: &[String],
) -> Result<WriteOutcome, WritebackError>
Expand description

Set multiple selected values on a multi-select list box (/Ff bit 22).

Writes /V as an array of text strings and rebuilds /I as the sorted, de-duplicated zero-based indices of the selected values within /Opt (PDF 32000-1 §12.7.4.4 — Acrobat writes both, and a consistent /I drives correct visual selection in viewers that honour it, disambiguating duplicate display strings). Per-option highlight appearance is viewer-native, so /NeedAppearances is set rather than synthesising an /AP.

Rejects read-only fields, non-choice fields, single-select list boxes, and (for non-editable fields) values absent from /Opt. Passing an empty values slice clears the selection (/V becomes an empty array and /I is removed).

Examples found in repository?
examples/fill_acroform_corpus.rs (line 52)
17fn fill(category: &str, bytes: &[u8]) -> (Vec<u8>, String) {
18    let mut doc = lopdf::Document::load_mem(bytes).expect("load fixture");
19    // `expect` per category since fixtures are known-good.
20    let manifest: String = match category {
21        "pure_text" => {
22            apply_field_value(&mut doc, "full_name", WriteValue::Text("Jane Doe")).unwrap();
23            r#"{"full_name":{"v":"Jane Doe","ap":true}}"#.into()
24        }
25        "multiline_comb" => {
26            apply_field_value(
27                &mut doc,
28                "notes",
29                WriteValue::Text("a long multiline note here"),
30            )
31            .unwrap();
32            apply_field_value(&mut doc, "postcode", WriteValue::Text("1234AB")).unwrap();
33            r#"{"notes":{"ap":true},"postcode":{"v":"1234AB","ap":true}}"#.into()
34        }
35        "checkbox" => {
36            apply_field_value(&mut doc, "agree", WriteValue::Checkbox(true)).unwrap();
37            r#"{"agree":{"v_name":"Yes","as":"Yes"}}"#.into()
38        }
39        "radio" => {
40            apply_field_value(&mut doc, "gender", WriteValue::Radio("M")).unwrap();
41            r#"{"gender":{"v_name":"M"}}"#.into()
42        }
43        "combo" => {
44            apply_field_value(&mut doc, "country", WriteValue::Choice("NL")).unwrap();
45            r#"{"country":{"v":"NL"}}"#.into()
46        }
47        "listbox_single" => {
48            apply_field_value(&mut doc, "city", WriteValue::Choice("Berlin")).unwrap();
49            r#"{"city":{"v":"Berlin"}}"#.into()
50        }
51        "multiselect" => {
52            apply_choice_multi(&mut doc, "languages", &["FR".to_string(), "EN".to_string()])
53                .unwrap();
54            r#"{"languages":{"v_array":["FR","EN"],"i":[0,3]}}"#.into()
55        }
56        "nonascii" => {
57            apply_field_value(&mut doc, "latin1_name", WriteValue::Text("Café Zürich")).unwrap();
58            apply_field_value(&mut doc, "cjk_name", WriteValue::Text("日本語")).unwrap();
59            r#"{"latin1_name":{"v":"Café Zürich","ap":true},"cjk_name":{"utf16":true,"needs_appearances":true}}"#.into()
60        }
61        "needappearances_missing_ap" => {
62            regenerate_appearances(&mut doc).unwrap();
63            r#"{"prefilled":{"v":"existing value","ap":true}}"#.into()
64        }
65        "signature" => {
66            // Not fillable — write nothing, just round-trip.
67            r#"{"signature1":{"fillable":false}}"#.into()
68        }
69        "xfa_shell" => {
70            apply_field_value(&mut doc, "acro_name", WriteValue::Text("hybrid")).unwrap();
71            r#"{"acro_name":{"v":"hybrid"}}"#.into()
72        }
73        "hierarchical" => {
74            apply_field_value(&mut doc, "address.street", WriteValue::Text("Damrak 1")).unwrap();
75            apply_field_value(&mut doc, "address.city", WriteValue::Text("Amsterdam")).unwrap();
76            r#"{"address.street":{"v":"Damrak 1"},"address.city":{"v":"Amsterdam"}}"#.into()
77        }
78        "readonly" => {
79            // ReadOnly — write rejected; round-trip the original.
80            r#"{"locked":{"writable":false}}"#.into()
81        }
82        other => panic!("unknown category {other}"),
83    };
84    let mut buf = Vec::new();
85    doc.save_to(&mut buf).expect("save filled");
86    (buf, manifest)
87}