diff --git a/plotly/src/plot.rs b/plotly/src/plot.rs index 9f970b92..826f82ba 100644 --- a/plotly/src/plot.rs +++ b/plotly/src/plot.rs @@ -586,9 +586,9 @@ impl PartialEq for Plot { mod tests { use std::path::PathBuf; - #[cfg(feature = "kaleido")] - use base64::{engine::general_purpose, Engine as _}; use serde_json::{json, to_value}; + #[cfg(not(target_os = "macos"))] + use {base64::engine::general_purpose, base64::Engine}; use super::*; use crate::Scatter; @@ -866,4 +866,31 @@ mod tests { const LEN: usize = 10; assert_eq!(expected[..LEN], image_svg[..LEN]); } + + #[cfg(target_os = "macos")] + #[test] + #[cfg(feature = "kaleido")] + fn save_surface_to_png() { + use crate::Surface; + let mut plot = Plot::new(); + let z_matrix = vec![ + vec![1.0, 2.0, 3.0], + vec![4.0, 5.0, 6.0], + vec![7.0, 8.0, 9.0], + ]; + let x_unique = vec![1.0, 2.0, 3.0]; + let y_unique = vec![4.0, 5.0, 6.0]; + let surface = Surface::new(z_matrix) + .x(x_unique) + .y(y_unique) + .name("Surface"); + + plot.add_trace(surface); + let dst = PathBuf::from("example.png"); + plot.write_image("example.png", ImageFormat::PNG, 800, 600, 1.0); + assert!(dst.exists()); + assert!(std::fs::remove_file(&dst).is_ok()); + assert!(!dst.exists()); + assert!(!plot.to_base64(ImageFormat::PNG, 1024, 680, 1.0).is_empty()); + } } diff --git a/plotly_kaleido/src/lib.rs b/plotly_kaleido/src/lib.rs index 91f1e09f..5ad78737 100644 --- a/plotly_kaleido/src/lib.rs +++ b/plotly_kaleido/src/lib.rs @@ -185,20 +185,34 @@ impl Kaleido { ) -> Result> { let p = self.cmd_path.to_str().unwrap(); + #[cfg(not(target_os = "macos"))] + let cmd_args = vec![ + "plotly", + "--disable-gpu", + "--allow-file-access-from-files", + "--disable-breakpad", + "--disable-dev-shm-usage", + "--disable-software-rasterizer", + "--single-process", + "--no-sandbox", + ]; + + // Add Kaleido issue #323 + #[cfg(target_os = "macos")] + let cmd_args = vec![ + "plotly", + "--allow-file-access-from-files", + "--disable-breakpad", + "--disable-dev-shm-usage", + "--disable-software-rasterizer", + "--single-process", + "--no-sandbox", + ]; + #[allow(clippy::zombie_processes)] let mut process = Command::new(p) .current_dir(self.cmd_path.parent().unwrap()) - .args([ - "plotly", - "--disable-gpu", - "--allow-file-access-from-files", - "--disable-breakpad", - "--disable-dev-shm-usage", - "--disable-software-rasterizer", - "--single-process", - "--disable-gpu", - "--no-sandbox", - ]) + .args(cmd_args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -213,7 +227,6 @@ impl Kaleido { .to_string() ) }); - { let plot_data = PlotData::new(plotly_data, format, width, height, scale).to_json(); let mut process_stdin = process.stdin.take().unwrap(); @@ -287,6 +300,47 @@ mod tests { .unwrap() } + #[cfg(target_os = "macos")] + fn create_test_surface() -> Value { + to_value(json!({ + "data": [ + { + "name": "Surface", + "type": "surface", + "x": [ + 1.0, + 2.0, + 3.0 + ], + "y": [ + 4.0, + 5.0, + 6.0 + ], + "z": [ + [ + 1.0, + 2.0, + 3.0 + ], + [ + 4.0, + 5.0, + 6.0 + ], + [ + 7.0, + 8.0, + 9.0 + ] + ] + } + ], + "layout": {} + })) + .unwrap() + } + #[test] fn can_find_kaleido_executable() { let _k = Kaleido::new(); @@ -378,4 +432,76 @@ mod tests { assert!(r.is_ok()); assert!(std::fs::remove_file(dst.as_path()).is_ok()); } + + // Issue #241 workaround until https://github.com/plotly/Kaleido/issues/323 is resolved + #[cfg(target_os = "macos")] + #[test] + fn save_surface_png() { + let test_plot = create_test_surface(); + let k = Kaleido::new(); + let dst = PathBuf::from("example.png"); + let r = k.save(dst.as_path(), &test_plot, "png", 1200, 900, 4.5); + assert!(r.is_ok()); + assert!(dst.exists()); + let metadata = std::fs::metadata(&dst).expect("Could not retrieve file metadata"); + let file_size = metadata.len(); + assert!(file_size > 0,); + assert!(std::fs::remove_file(dst.as_path()).is_ok()); + } + #[cfg(target_os = "macos")] + #[test] + fn save_surface_jpeg() { + let test_plot = create_test_surface(); + let k = Kaleido::new(); + let dst = PathBuf::from("example.jpeg"); + let r = k.save(dst.as_path(), &test_plot, "jpeg", 1200, 900, 4.5); + assert!(r.is_ok()); + assert!(dst.exists()); + let metadata = std::fs::metadata(&dst).expect("Could not retrieve file metadata"); + let file_size = metadata.len(); + assert!(file_size > 0,); + assert!(std::fs::remove_file(dst.as_path()).is_ok()); + } + #[cfg(target_os = "macos")] + #[test] + fn save_surface_webp() { + let test_plot = create_test_surface(); + let k = Kaleido::new(); + let dst = PathBuf::from("example.webp"); + let r = k.save(dst.as_path(), &test_plot, "webp", 1200, 900, 4.5); + assert!(r.is_ok()); + assert!(dst.exists()); + let metadata = std::fs::metadata(&dst).expect("Could not retrieve file metadata"); + let file_size = metadata.len(); + assert!(file_size > 0,); + assert!(std::fs::remove_file(dst.as_path()).is_ok()); + } + #[cfg(target_os = "macos")] + #[test] + fn save_surface_svg() { + let test_plot = create_test_surface(); + let k = Kaleido::new(); + let dst = PathBuf::from("example.svg"); + let r = k.save(dst.as_path(), &test_plot, "svg", 1200, 900, 4.5); + assert!(r.is_ok()); + assert!(dst.exists()); + let metadata = std::fs::metadata(&dst).expect("Could not retrieve file metadata"); + let file_size = metadata.len(); + assert!(file_size > 0,); + assert!(std::fs::remove_file(dst.as_path()).is_ok()); + } + #[cfg(target_os = "macos")] + #[test] + fn save_surface_pdf() { + let test_plot = create_test_surface(); + let k = Kaleido::new(); + let dst = PathBuf::from("example.pdf"); + let r = k.save(dst.as_path(), &test_plot, "pdf", 1200, 900, 4.5); + assert!(r.is_ok()); + assert!(dst.exists()); + let metadata = std::fs::metadata(&dst).expect("Could not retrieve file metadata"); + let file_size = metadata.len(); + assert!(file_size > 0,); + assert!(std::fs::remove_file(dst.as_path()).is_ok()); + } }