diff --git a/crates/pdf/src/export.rs b/crates/pdf/src/export.rs index dbcbe296..d92fc242 100644 --- a/crates/pdf/src/export.rs +++ b/crates/pdf/src/export.rs @@ -1259,7 +1259,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)); + } } if let Some(image) = &img { // Share the original encoded payload. HashMap checks exact bytes after hashing, diff --git a/crates/pdf/src/tests.rs b/crates/pdf/src/tests.rs index 521e4444..9ac53d05 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.