diff --git a/crates/zotero/src/bridge.rs b/crates/zotero/src/bridge.rs index c95e67a..b5d00bb 100644 --- a/crates/zotero/src/bridge.rs +++ b/crates/zotero/src/bridge.rs @@ -568,6 +568,10 @@ fn remove_empty_note(s: &mut Session, f: &ZField) -> Option { for b in blocks { if let Some(p) = b.as_para() { rest.push_str(&p.text); + // A picture, shape, equation... is content too, even though it has no text. + if !p.objects.iter().all(|o| o.is_marker() || matches!(o, InlineObject::NoteRef { .. })) { + return None; + } } } if rest.chars().any(|c| c != OBJ && !c.is_whitespace()) { diff --git a/crates/zotero/src/tests.rs b/crates/zotero/src/tests.rs index a4a365d..4a8d581 100644 --- a/crates/zotero/src/tests.rs +++ b/crates/zotero/src/tests.rs @@ -512,6 +512,22 @@ fn a_note_with_other_text_stays_when_its_citation_leaves() { assert!(z.call("Document_convert", json!([1, [999], [], [1], 1])).is_err()); } +#[test] +fn a_note_with_a_picture_stays_when_its_citation_leaves() { + let mut z = Z::new("Text.", 5); + z.begin(); + let a = z.cite(1, "Doe, Title."); + let pid = *z.s.doc.parts.keys().next().unwrap(); + let p = z.s.doc.para_mut(StoryRef::Part(pid), &Path::top(0)).unwrap(); + let n = p.len(); + let image = InlineObject::Image { media: "m".into(), w: 1.0, h: 1.0, alt: String::new(), float: Default::default(), crop: [0.0; 4], ole: None }; + p.insert_object(n, image, &Default::default()).unwrap(); + z.ok("Field_convert", json!([1, a, "ReferenceMark", 0])); + assert_eq!(z.s.doc.parts.len(), 1); + assert!(z.s.doc.para(StoryRef::Part(pid), &Path::top(0)).unwrap().objects.iter().any(|o| matches!(o, InlineObject::Image { .. }))); + assert!(z.text().ends_with("Doe, Title.")); +} + /// Zotero reads a new bibliography field back before filling it, and unlinks it when it is /// empty (it takes that for a bibliography the user deleted). Seen with Zotero 10: Add/Edit /// Bibliography did nothing.