From 773343a173c1c5365d700c1366334cf215219433 Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:33:23 +0200 Subject: [PATCH 1/3] re-export ImageFormat from plotly_static into plotly package - fix plotly_static example - bump plotly_static and remove patch version from plotly_static when used in plotly crate Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- CHANGELOG.md | 6 +++++ README.md | 6 +++-- .../src/fundamentals/static_image_export.md | 3 +-- plotly/Cargo.toml | 2 +- plotly/src/lib.rs | 9 ++++---- plotly_static/Cargo.toml | 2 +- plotly_static/README.md | 4 ++-- plotly_static/examples/README.md | 22 +++++++++---------- plotly_static/src/lib.rs | 2 +- 9 files changed, 32 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebee4531..ce7869a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.13.5] - 2025-07-30 + +### Fixed + +- [[#345](https://github.com/plotly/plotly.rs/pull/345)] Re-export `ImageFormat` from `plotly_static` into `plotly` + ## [0.13.4] - 2025-07-17 ### Fixed diff --git a/README.md b/README.md index e0c80112..461356c1 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ plotly = { version = "0.13", features = ["static_export_default"] } This supports PNG, JPEG, WEBP, SVG, and PDF formats: ```rust -use plotly::{Plot, Scatter, ImageFormat}; +use plotly::{Plot, Scatter,ImageFormat}; let mut plot = Plot::new(); plot.add_trace(Scatter::new(vec![0, 1, 2], vec![2, 1, 0])); @@ -164,7 +164,9 @@ Kaleido binaries are available on Github [release page](https://github.com/plotl ## Usage Within a WASM Environment -`Plotly.rs` can be used with a WASM-based frontend framework. The needed dependencies are automatically enabled for `wasm32` targets at compile time and there is no longer a need for the custom `wasm` flag in this crate. Note that the `kaleido` and `plotly_static` features are not supported in WASM environments and will throw a compilation error if enabled. +`Plotly.rs` can be used with a WASM-based frontend framework. Note that the `kaleido` and `plotly_static` static export features are not supported in WASM environments and will throw a compilation error if used. + +The needed dependencies are automatically enabled for `wasm32` targets at compile time and there is no longer a need for the custom `wasm` flag in this crate. First, make sure that you have the Plotly JavaScript library in your base HTML template: diff --git a/docs/book/src/fundamentals/static_image_export.md b/docs/book/src/fundamentals/static_image_export.md index 29ec2cbd..81dab8d0 100644 --- a/docs/book/src/fundamentals/static_image_export.md +++ b/docs/book/src/fundamentals/static_image_export.md @@ -57,8 +57,7 @@ plotly = { version = "0.13", features = ["static_export_default"] } ### Simple Export ```rust -use plotly::{Plot, Scatter}; -use plotly::plotly_static::ImageFormat; +use plotly::{Plot, Scatter, ImageFormat}; let mut plot = Plot::new(); plot.add_trace(Scatter::new(vec![1, 2, 3], vec![4, 5, 6])); diff --git a/plotly/Cargo.toml b/plotly/Cargo.toml index 69dac899..f3400bb3 100644 --- a/plotly/Cargo.toml +++ b/plotly/Cargo.toml @@ -52,7 +52,7 @@ dyn-clone = "1" erased-serde = "0.4" image = { version = "0.25", optional = true } plotly_derive = { version = "0.13", path = "../plotly_derive" } -plotly_static = { version = "0.0.3", path = "../plotly_static", optional = true } +plotly_static = { version = "0.0", path = "../plotly_static", optional = true } plotly_kaleido = { version = "0.13", path = "../plotly_kaleido", optional = true } ndarray = { version = "0.16", optional = true } once_cell = "1" diff --git a/plotly/src/lib.rs b/plotly/src/lib.rs index 4c75c15f..a06f04d5 100644 --- a/plotly/src/lib.rs +++ b/plotly/src/lib.rs @@ -57,10 +57,6 @@ pub use common::color; pub use configuration::Configuration; pub use layout::Layout; pub use plot::{Plot, Trace, Traces}; -#[cfg(feature = "kaleido")] -pub use plotly_kaleido::ImageFormat; -#[cfg(feature = "plotly_static")] -pub use plotly_static; // Also provide easy access to modules which contain additional trace-specific types pub use traces::{ box_plot, contour, heat_map, histogram, image, mesh3d, sankey, scatter, scatter3d, @@ -75,6 +71,11 @@ pub use traces::{ pub trait Restyle: serde::Serialize {} pub trait Relayout {} +#[cfg(feature = "kaleido")] +pub use plotly_kaleido::ImageFormat; +#[cfg(feature = "plotly_static")] +pub use plotly_static::{self, ImageFormat}; + // Not public API. #[doc(hidden)] mod private; diff --git a/plotly_static/Cargo.toml b/plotly_static/Cargo.toml index efbd71a1..6b43502f 100644 --- a/plotly_static/Cargo.toml +++ b/plotly_static/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plotly_static" -version = "0.0.3" +version = "0.0.4" description = "Export Plotly graphs to static images using WebDriver" authors = ["Andrei Gherghescu andrei-ng@protonmail.com"] license = "MIT" diff --git a/plotly_static/README.md b/plotly_static/README.md index a0e653e9..27eaa6e9 100644 --- a/plotly_static/README.md +++ b/plotly_static/README.md @@ -56,7 +56,7 @@ Add to your `Cargo.toml`: ```toml [dependencies] -plotly_static = { version = "0.0.3", features = ["chromedriver", "webdriver_download"] } +plotly_static = { version = "0.0.4", features = ["chromedriver", "webdriver_download"] } serde_json = "1.0" ``` @@ -155,4 +155,4 @@ Similar examples are available in the [Plotly.rs package](https://github.com/plo ## License -This package is licensed under the MIT License. \ No newline at end of file +This package is licensed under the MIT License. diff --git a/plotly_static/examples/README.md b/plotly_static/examples/README.md index c10a2d85..959f62b1 100644 --- a/plotly_static/examples/README.md +++ b/plotly_static/examples/README.md @@ -8,17 +8,17 @@ This example demonstrates how to use the `plotly_static` crate with `clap` to cr Export a plot from a JSON file (using Chrome driver): ```bash -cargo run --example main --features chromedriver -- -i sample_plot.json -o my_plot -f png +cargo run --example generate_static --features chromedriver -- -i sample_plot.json -o my_plot -f png ``` Export a plot from a JSON file (using Firefox/Gecko driver): ```bash -cargo run --example main --features geckodriver -- -i sample_plot.json -o my_plot -f png +cargo run --example generate_static --features geckodriver -- -i sample_plot.json -o my_plot -f png ``` Export a plot from stdin: ```bash -cat sample_plot.json | cargo run --example main --features chromedriver -- -f svg -o output +cat sample_plot.json | cargo run --example generate_static --features chromedriver -- -f svg -o output ``` ### Web Driver Options @@ -31,10 +31,10 @@ The example supports two different web drivers for rendering plots: You must specify one of these features when running the example. For example: ```bash # Use Chrome driver -cargo run --example main --features chromedriver -- -i plot.json -o output.png +cargo run --example generate_static --features chromedriver -- -i plot.json -o output.png # Use Firefox driver -cargo run --example main --features geckodriver -- -i plot.json -o output.png +cargo run --example generate_static --features geckodriver -- -i plot.json -o output.png ``` ### Logging @@ -43,13 +43,13 @@ The example uses `env_logger` for logging. You can enable different log levels u ```bash # Enable info level logging -RUST_LOG=info cargo run --example main --features chromedriver -- -i sample_plot.json -o my_plot -f png +RUST_LOG=info cargo run --example generate_static --features chromedriver -- -i sample_plot.json -o my_plot -f png # Enable debug level logging for more verbose output -RUST_LOG=debug cargo run --example main --features geckodriver -- -i sample_plot.json -o my_plot -f png +RUST_LOG=debug cargo run --example generate_static --features geckodriver -- -i sample_plot.json -o my_plot -f png # Enable all logging levels -RUST_LOG=trace cargo run --example main --features chromedriver -- -i sample_plot.json -o my_plot -f png +RUST_LOG=trace cargo run --example generate_static --features chromedriver -- -i sample_plot.json -o my_plot -f png ``` ### Command Line Options @@ -66,18 +66,18 @@ RUST_LOG=trace cargo run --example main --features chromedriver -- -i sample_plo Export to PNG with custom dimensions: ```bash -cargo run --example main --features chromedriver -- -i sample_plot.json -o plot -f png --width 1200 --height 800 +cargo run --example generate_static --features chromedriver -- -i sample_plot.json -o plot -f png --width 1200 --height 800 ``` Export to SVG from stdin: ```bash echo '{"data":[{"type":"scatter","x":[1,2,3],"y":[4,5,6]}],"layout":{}}' | \ -cargo run --example main --features geckodriver -- -f svg -o scatter_plot +cargo run --example generate_static --features geckodriver -- -f svg -o scatter_plot ``` Export to PDF with high resolution: ```bash -cargo run --example main --features chromedriver -- -i sample_plot.json -o report -f pdf --width 1600 --height 1200 -s 2.0 +cargo run --example generate_static --features chromedriver -- -i sample_plot.json -o report -f pdf --width 1600 --height 1200 -s 2.0 ``` ### JSON Format diff --git a/plotly_static/src/lib.rs b/plotly_static/src/lib.rs index 3f6dbbf1..f11c9432 100644 --- a/plotly_static/src/lib.rs +++ b/plotly_static/src/lib.rs @@ -74,7 +74,7 @@ //! //! ```toml //! [dependencies] -//! plotly_static = { version = "0.0.3", features = ["chromedriver", "webdriver_download"] } +//! plotly_static = { version = "0.0.4", features = ["chromedriver", "webdriver_download"] } //! ``` //! //! ## Advanced Usage From c13db04f6ebe276d0f5bf3d09a1f4cba2eaa5a67 Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Wed, 30 Jul 2025 22:08:47 +0200 Subject: [PATCH 2/3] remove usage of getrandom in rand dependency and in our crate - we use rand internally to create random names for div's and for file names, for that reason highly performant crypto and/or efficinet entropy generators are not a priority. Using rand with minimal features and no `getrandom` simplifies usage for downstream users as they don't need the custom WASM rustc flags for `getrandom` and `wasm_js` feature - bump versions Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- .cargo/config.toml | 2 -- CHANGELOG.md | 3 ++- plotly/Cargo.toml | 8 +++++--- plotly/src/plot.rs | 12 ++++++------ plotly_derive/Cargo.toml | 2 +- plotly_kaleido/Cargo.toml | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) delete mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 2e07606d..00000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] diff --git a/CHANGELOG.md b/CHANGELOG.md index ce7869a1..85f3faad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.13.5] - 2025-07-30 +## [0.13.5] - 2025-07-31 ### Fixed +- [[#346](https://github.com/plotly/plotly.rs/pull/346)] Remove usage of `getrandom` for `rand` dependency and for this crate - [[#345](https://github.com/plotly/plotly.rs/pull/345)] Re-export `ImageFormat` from `plotly_static` into `plotly` ## [0.13.4] - 2025-07-17 diff --git a/plotly/Cargo.toml b/plotly/Cargo.toml index f3400bb3..a744ceea 100644 --- a/plotly/Cargo.toml +++ b/plotly/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plotly" -version = "0.13.4" +version = "0.13.5" description = "A plotting library powered by Plotly.js" authors = [ "Ioannis Giagkiozis ", @@ -60,10 +60,12 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_repr = "0.1" serde_with = ">=2, <4" -rand = "0.9" +rand = { version = "0.9", default-features = false, features = [ + "small_rng", + "alloc", +] } [target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.3", features = ["wasm_js"] } wasm-bindgen-futures = { version = "0.4" } wasm-bindgen = { version = "0.2" } serde-wasm-bindgen = { version = "0.6.3" } diff --git a/plotly/src/plot.rs b/plotly/src/plot.rs index 44b3b9db..50371149 100644 --- a/plotly/src/plot.rs +++ b/plotly/src/plot.rs @@ -9,7 +9,8 @@ use plotly_kaleido::ImageFormat; use plotly_static::ImageFormat; use rand::{ distr::{Alphanumeric, SampleString}, - rng, + rngs::SmallRng, + SeedableRng, }; use serde::Serialize; @@ -265,12 +266,11 @@ impl Plot { #[cfg(all(not(target_family = "wasm"), not(target_os = "android")))] pub fn show(&self) { use std::env; - let rendered = self.render(); // Set up the temp file with a unique filename. let mut temp = env::temp_dir(); - let mut plot_name = Alphanumeric.sample_string(&mut rng(), 22); + let mut plot_name = Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 22); plot_name.push_str(".html"); plot_name = format!("plotly_{plot_name}"); temp.push(plot_name); @@ -313,7 +313,7 @@ impl Plot { // Set up the temp file with a unique filename. let mut temp = env::temp_dir(); - let mut plot_name = Alphanumeric.sample_string(&mut rng(), 22); + let mut plot_name = Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 22); plot_name.push_str(".html"); plot_name = format!("plotly_{plot_name}"); temp.push(plot_name); @@ -371,13 +371,13 @@ impl Plot { pub fn to_inline_html(&self, plot_div_id: Option<&str>) -> String { let plot_div_id = match plot_div_id { Some(id) => id.to_string(), - None => Alphanumeric.sample_string(&mut rng(), 20), + None => Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 20), }; self.render_inline(&plot_div_id) } fn to_jupyter_notebook_html(&self) -> String { - let plot_div_id = Alphanumeric.sample_string(&mut rng(), 20); + let plot_div_id = Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 20); let tmpl = JupyterNotebookPlotTemplate { plot: self, diff --git a/plotly_derive/Cargo.toml b/plotly_derive/Cargo.toml index e4d8e4e6..159cc598 100644 --- a/plotly_derive/Cargo.toml +++ b/plotly_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plotly_derive" -version = "0.13.4" +version = "0.13.5" description = "Internal proc macro crate for Plotly-rs." authors = ["Ioannis Giagkiozis "] license = "MIT" diff --git a/plotly_kaleido/Cargo.toml b/plotly_kaleido/Cargo.toml index d2d54c4e..65fb87e7 100644 --- a/plotly_kaleido/Cargo.toml +++ b/plotly_kaleido/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plotly_kaleido" -version = "0.13.4" +version = "0.13.5" description = "Additional output format support for plotly using Kaleido" authors = [ "Ioannis Giagkiozis ", From 4b9309857a3750810fcf05721747de321107ee09 Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:06:11 +0200 Subject: [PATCH 3/3] make sure that SmallRng uses a unique seed per call Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- plotly/src/plot.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/plotly/src/plot.rs b/plotly/src/plot.rs index 50371149..130b75ea 100644 --- a/plotly/src/plot.rs +++ b/plotly/src/plot.rs @@ -1,3 +1,5 @@ +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::{SystemTime, UNIX_EPOCH}; use std::{fs::File, io::Write, path::Path}; use askama::Template; @@ -16,6 +18,8 @@ use serde::Serialize; use crate::{layout::Frame, Configuration, Layout}; +static SEED_COUNTER: AtomicU64 = AtomicU64::new(0); + #[derive(Template)] #[template(path = "plot.html", escape = "none")] struct PlotTemplate<'a> { @@ -270,7 +274,8 @@ impl Plot { // Set up the temp file with a unique filename. let mut temp = env::temp_dir(); - let mut plot_name = Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 22); + let mut plot_name = + Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(Self::generate_seed()), 22); plot_name.push_str(".html"); plot_name = format!("plotly_{plot_name}"); temp.push(plot_name); @@ -313,7 +318,8 @@ impl Plot { // Set up the temp file with a unique filename. let mut temp = env::temp_dir(); - let mut plot_name = Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 22); + let mut plot_name = + Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(Self::generate_seed()), 22); plot_name.push_str(".html"); plot_name = format!("plotly_{plot_name}"); temp.push(plot_name); @@ -371,13 +377,16 @@ impl Plot { pub fn to_inline_html(&self, plot_div_id: Option<&str>) -> String { let plot_div_id = match plot_div_id { Some(id) => id.to_string(), - None => Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 20), + None => { + Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(Self::generate_seed()), 20) + } }; self.render_inline(&plot_div_id) } fn to_jupyter_notebook_html(&self) -> String { - let plot_div_id = Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(42), 20); + let plot_div_id = + Alphanumeric.sample_string(&mut SmallRng::seed_from_u64(Self::generate_seed()), 20); let tmpl = JupyterNotebookPlotTemplate { plot: self, @@ -869,6 +878,17 @@ impl Plot { .spawn() .expect(DEFAULT_HTML_APP_NOT_FOUND); } + + /// Generate unique seeds for SmallRng such that file names and div names + /// are unique random for each call + pub(crate) fn generate_seed() -> u64 { + let time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_nanos() as u64; + let counter = SEED_COUNTER.fetch_add(1, Ordering::Relaxed); + time ^ counter + } } impl PartialEq for Plot {