mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 11:37:59 -06:00
fix(xfdf): read an indirect /F when deciding a comment is locked against import
This commit is contained in:
@@ -1037,8 +1037,9 @@ const LOCKED: i64 = 128;
|
||||
/// An existing annotation an import must never replace, move or unlock: one the user locked, a
|
||||
/// redaction mark (replacing it would drop the region the user marked to remove), or a form
|
||||
/// widget or field (replacing it would set the field's value and policy around the form setter).
|
||||
fn import_protected(x: &Dict) -> bool {
|
||||
x.int(b"F").unwrap_or(0) & LOCKED != 0 || matches!(x.name(b"Subtype"), Some(b"Redact" | b"Widget")) || x.contains(b"FT")
|
||||
fn import_protected(doc: &Document, x: &Dict) -> bool {
|
||||
let flags = x.get(b"F").and_then(|f| doc.resolve(f).as_int()).unwrap_or(0);
|
||||
flags & LOCKED != 0 || matches!(x.name(b"Subtype"), Some(b"Redact" | b"Widget")) || x.contains(b"FT")
|
||||
}
|
||||
|
||||
fn place(doc: &mut Document, page_ref: ObjRef, mut d: Dict) -> Result<ObjRef, DataError> {
|
||||
@@ -1089,7 +1090,7 @@ fn place(doc: &mut Document, page_ref: ObjRef, mut d: Dict) -> Result<ObjRef, Da
|
||||
for a in annots {
|
||||
let existing = a.as_ref().and_then(|r| doc.get(r).as_dict().cloned());
|
||||
let same = nm.is_some() && existing.as_ref().and_then(|x| x.get(b"NM").and_then(|n| doc.resolve(n).as_string().map(|s| s.to_text()))) == nm;
|
||||
if same && existing.as_ref().is_some_and(import_protected) {
|
||||
if same && existing.as_ref().is_some_and(|x| import_protected(doc, x)) {
|
||||
clash = true;
|
||||
kept.push(a);
|
||||
} else if same {
|
||||
|
||||
@@ -1842,3 +1842,26 @@ fn xfdf_line_ending_names_are_escaped() {
|
||||
let want = String::from_utf8(hostile).unwrap();
|
||||
assert_eq!((el.attribute("head"), el.attribute("tail")), (Some(want.as_str()), Some(want.as_str())));
|
||||
}
|
||||
|
||||
/// A comment locked through an indirect `/F` is as protected from a same-named import as one
|
||||
/// with the flag written inline.
|
||||
#[test]
|
||||
fn a_comment_locked_by_an_indirect_flag_is_not_replaced_by_an_import() {
|
||||
let mut doc = blank();
|
||||
let page = pdfcraft_model::pages(&doc)[0].obj;
|
||||
let flag = doc.add(Object::Int(128)); // Locked
|
||||
let mut note = Dict::new();
|
||||
note.set(b"Type".to_vec(), Object::name("Annot"));
|
||||
note.set(b"Subtype".to_vec(), Object::name("Text"));
|
||||
note.set(b"NM".to_vec(), PdfString::text("n1"));
|
||||
note.set(b"Rect".to_vec(), Object::Array(vec![10.into(), 10.into(), 30.into(), 30.into()]));
|
||||
note.set(b"F".to_vec(), Object::Ref(flag));
|
||||
let note = doc.add(Object::Dict(note));
|
||||
doc.update_dict(page, |p| p.set(b"Annots".to_vec(), Object::Array(vec![Object::Ref(note)]))).unwrap();
|
||||
let before = doc.get(note);
|
||||
let xfdf = br#"<xfdf><annots><text page="0" name="n1" rect="0,0,1,1"/></annots></xfdf>"#;
|
||||
assert_eq!(import(&mut doc, xfdf).unwrap().comments, 1);
|
||||
let annots = doc.get(page).as_dict().and_then(|p| p.get(b"Annots").and_then(Object::as_array).cloned()).unwrap();
|
||||
assert!(annots.contains(&Object::Ref(note)), "the locked comment was replaced");
|
||||
assert_eq!(doc.get(note), before, "the locked comment was changed");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user