diff --git a/crates/annot/src/lib.rs b/crates/annot/src/lib.rs index d0a0ae09..4e8aceb2 100644 --- a/crates/annot/src/lib.rs +++ b/crates/annot/src/lib.rs @@ -741,6 +741,10 @@ pub fn is_fill_sign(doc: &Document, d: &Dict) -> bool { subtype == b"Ink" && text_value(doc, d, b"Subj").as_deref() == Some("Signature") } +fn annotation_flags(doc: &Document, d: &Dict) -> i64 { + d.get(b"F").and_then(|f| doc.resolve(f).as_int()).unwrap_or(0) +} + /// Whether any page has a Fill & Sign annotation that flatten would bake in (not hidden). pub fn has_visible_fill_sign(doc: &Document) -> bool { let Ok(pages) = page_refs(doc) else { return false }; @@ -748,7 +752,7 @@ pub fn has_visible_fill_sign(doc: &Document) -> bool { for entry in annots(doc, page) { let obj = doc.resolve(&entry); let Some(d) = obj.as_dict() else { continue }; - let flags = d.get(b"F").and_then(|f| doc.resolve(f).as_int()).unwrap_or(0); + let flags = annotation_flags(doc, d); if flags & (FLAG_HIDDEN | FLAG_NO_VIEW) != 0 { continue; } @@ -1504,7 +1508,7 @@ fn reply( } fn flags(doc: &Document, r: ObjRef) -> i64 { - doc.get(r).as_dict().and_then(|d| d.get(b"F").and_then(|f| doc.resolve(f).as_f64())).unwrap_or(0.0) as i64 + doc.get(r).as_dict().map(|d| annotation_flags(doc, d)).unwrap_or(0) } /// Refuse to change a locked comment (Acrobat: Properties ▸ Locked). @@ -1848,7 +1852,7 @@ pub fn summaries(doc: &Document) -> Vec { color, state: text_value(doc, d, b"State"), quads, - locked: d.get(b"F").and_then(|f| doc.resolve(f).as_int()).unwrap_or(0) & FLAG_LOCKED != 0, + locked: annotation_flags(doc, d) & FLAG_LOCKED != 0, intent: d.get(b"IT").and_then(|o| doc.resolve(o).as_name().map(|n| String::from_utf8_lossy(n).into_owned())), }); } @@ -1934,7 +1938,7 @@ pub fn props(doc: &Document, page: usize, index: usize) -> Option { icon: (subtype == "Text").then(|| d.name(b"Name").and_then(|n| NoteIcon::from_name(&String::from_utf8_lossy(n))).unwrap_or(NoteIcon::Note)), modified: text_value(doc, d, b"M"), restylable: appearance::build(d).is_some(), - locked: d.get(b"F").and_then(|f| doc.resolve(f).as_int()).unwrap_or(0) & FLAG_LOCKED != 0, + locked: annotation_flags(doc, d) & FLAG_LOCKED != 0, subtype, endings: line_endings_of(d), }) diff --git a/crates/annot/src/tests.rs b/crates/annot/src/tests.rs index 93cc6497..5cd9614a 100644 --- a/crates/annot/src/tests.rs +++ b/crates/annot/src/tests.rs @@ -374,6 +374,20 @@ fn editing_a_callout_refits_the_box_and_keeps_the_leader() { assert_eq!(ap_content(&doc, d).matches(" Tj").count(), appearance::wrap(long, 12.0, 200.0 - 2.0 * 4.0).len(), "nothing is clipped"); } +#[test] +fn real_annotation_flags_do_not_lock_comments() { + let mut doc = fixture(); + let page = page_refs(&doc).unwrap()[0]; + let mut entries = annots(&doc, page); + let Object::Dict(annotation) = &mut entries[0] else { panic!("fixture annotation is inline") }; + annotation.set(b"F".to_vec(), Object::Real(128.5)); + set_annots(&mut doc, page, entries).unwrap(); + + assert!(!summaries(&doc)[0].locked, "a non-integer /F is outside the annotation flag domain"); + assert!(!props(&doc, 0, 0).unwrap().locked); + assert!(set_info(&mut doc, 0, 0, Some("Ada"), None, None, &meta("")).is_ok()); +} + #[test] fn a_locked_text_box_still_refits_its_text() { let mut doc = fixture();