diff --git a/contributors/people.toml b/contributors/people.toml index 7420dafd08..30077a230f 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -248,3 +248,6 @@ sync_github_name = true [people.adi-1108] real_name = "Aditya Vishwakarma" sync_github_name = true + +[people.benandez] +real_name = "Martin Ball" diff --git a/crates/engine/src/web_cmds.rs b/crates/engine/src/web_cmds.rs index 894cdca40b..f1d8b23317 100644 --- a/crates/engine/src/web_cmds.rs +++ b/crates/engine/src/web_cmds.rs @@ -1086,6 +1086,14 @@ fn quick_export(s: &mut Session, p: &Value) -> Result { target } }; + // The name typed in the save dialog wins over Export Preferences' format: Quick Export as PNG + // renamed to `photo.jpeg` must write a JPEG, not PNG bytes upload checks refuse (#8519). + let fmt = match crate::file_cmds::extension(&path).as_deref() { + None => fmt, + Some("jpg" | "jpeg") => "jpg".into(), + Some(e @ ("png" | "gif" | "webp")) => e.into(), + Some(e) => return Err(bad(cmd, format!("Quick Export writes PNG, JPEG, GIF or WebP, not .{e}; use Export As"))), + }; let d = s.active().ok_or(EngineError::NoDocument)?; let doc = d.doc.clone(); if fmt == "exr" { diff --git a/crates/engine/src/web_cmds/tests.rs b/crates/engine/src/web_cmds/tests.rs index 795c3c7b7e..9ba644719d 100644 --- a/crates/engine/src/web_cmds/tests.rs +++ b/crates/engine/src/web_cmds/tests.rs @@ -479,6 +479,33 @@ fn quick_export_never_replaces_the_documents_own_file_unasked() { s.execute("prefs.reset", json!({"path": "export"})).unwrap(); } +/// #8519: Quick Export as PNG renamed to `photo.jpeg` in the save dialog wrote PNG bytes under a +/// JPEG name, which upload checks refuse. A Quick Export extension picks the format; another +/// image extension is refused; no extension keeps Export Preferences' format. +#[test] +fn quick_export_writes_the_format_its_path_names() { + let dir = tmp("quick_ext"); + let mut s = session(8); + s.execute("file.export.exportPreferences", json!({"quickExportFormat": "png"})).unwrap(); + for (name, format, magic) in [ + ("a.jpeg", "jpg", &b"\xFF\xD8\xFF"[..]), + ("b.JPG", "jpg", b"\xFF\xD8\xFF"), + ("c.gif", "gif", b"GIF8"), + ("d.webp", "webp", b"RIFF"), + ("e", "png", b"\x89PNG"), + ] { + let path = format!("{dir}/{name}"); + let r = s.execute("file.export.quickExport", json!({"path": path})).unwrap(); + assert_eq!(r["format"], json!(format), "{name}"); + assert!(std::fs::read(&path).unwrap().starts_with(magic), "{name} holds {format} data"); + } + let tif = format!("{dir}/f.tif"); + let e = s.execute("file.export.quickExport", json!({"path": tif})).unwrap_err().to_string(); + assert!(e.contains("Export As"), "{e}"); + assert!(!std::path::Path::new(&tif).exists()); + s.execute("prefs.reset", json!({"path": "export"})).unwrap(); +} + #[test] fn same_file_sees_through_other_names_for_one_file() { let dir = tmp("same_file");