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}