diff --git a/crates/pdf/src/export.rs b/crates/pdf/src/export.rs index e0b98726f..7ce95d34f 100644 --- a/crates/pdf/src/export.rs +++ b/crates/pdf/src/export.rs @@ -1212,7 +1212,13 @@ impl Exporter<'_> { _ => lossless(&data, fmt, mode), }; if img.is_none() { - self.warn(format!("image `{}` could not be decoded and was skipped", asset.name)); + if data.is_empty() { + // A linked file that wasn't found when the document was opened: nothing to decode. + let at = asset.link.as_deref().map(|l| format!(" ({l})")).unwrap_or_default(); + self.warn(format!("image `{}` is missing{at} and was skipped", asset.name)); + } else { + self.warn(format!("image `{}` could not be decoded and was skipped", asset.name)); + } } self.images.insert(id, img.clone()); img diff --git a/crates/pdf/src/tests.rs b/crates/pdf/src/tests.rs index 521e4444e..9ac53d057 100644 --- a/crates/pdf/src/tests.rs +++ b/crates/pdf/src/tests.rs @@ -240,6 +240,20 @@ fn place(d: &mut Document, data: Vec, px: (u32, u32)) { d.insert_item(SpreadRef::Doc(0), it, None).unwrap(); } +/// A placed image whose linked file wasn't found has no data: the warning says it is missing, +/// not that it could not be decoded. +#[test] +fn a_missing_linked_image_is_reported_as_missing() { + let mut d = doc_with_text("still here"); + place(&mut d, Vec::new(), (64, 64)); + if let Some(a) = d.assets.values_mut().next() { + std::sync::Arc::make_mut(a).link = Some("/gone/curtains.jpeg".into()); + } + let r = export_pdf_with_report(&d, &Cache::new(), &PdfOptions::default()).unwrap(); + assert!(r.warnings.iter().any(|w| w.contains("is missing (/gone/curtains.jpeg) and was skipped")), "{:?}", r.warnings); + assert!(!r.warnings.iter().any(|w| w.contains("could not be decoded")), "{:?}", r.warnings); +} + /// krilla reads PNG, GIF and WebP lazily, so a damaged one passed the exporter's "does it decode" /// check and failed the whole export when the PDF was written ("PDF writer error: … unexpected /// end of file"). A cut-off download must be skipped with a warning, as a damaged TIFF is.