Skip to content

Commit 4cfaaa2

Browse files
committed
cleanup
Signed-off-by: Andrei Gherghescu <[email protected]>
1 parent 9a9c327 commit 4cfaaa2

File tree

13 files changed

+424
-229
lines changed

13 files changed

+424
-229
lines changed

docs/book/src/fundamentals/static_image_export.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ plot1.write_image_with_exporter(&mut exporter, "plot1", ImageFormat::PNG, 800, 6
9191
.expect("Failed to export plot1");
9292
plot2.write_image_with_exporter(&mut exporter, "plot2", ImageFormat::JPEG, 800, 600, 1.0)
9393
.expect("Failed to export plot2");
94+
95+
// Always close the exporter to ensure proper release of WebDriver resources
96+
exporter.close();
9497
```
9598

9699
## Supported Formats
@@ -129,8 +132,13 @@ let base64_data = plot.to_base64_with_exporter(&mut exporter, ImageFormat::PNG,
129132
// Get SVG data (vector format, scalable)
130133
let svg_data = plot.to_svg_with_exporter(&mut exporter, 400, 300, 1.0)
131134
.expect("Failed to export plot");
135+
136+
// Always close the exporter to ensure proper release of WebDriver resources
137+
exporter.close();
132138
```
133139

140+
Always call `close()` on the exporter to ensure proper release of WebDriver resources. Due to the nature of WebDriver implementation, close has to be called as resources cannot be automatically dropped or released.
141+
134142
## Advanced Configuration
135143

136144
### Custom WebDriver Configuration
@@ -150,6 +158,10 @@ let mut exporter = StaticExporterBuilder::default()
150158
])
151159
.build()
152160
.expect("Failed to create StaticExporter");
161+
162+
// Always close the exporter to ensure proper release of WebDriver resources
163+
exporter.close();
164+
153165
```
154166

155167
### Parallel Usage
@@ -172,6 +184,9 @@ let mut exporter = StaticExporterBuilder::default()
172184
.webdriver_port(get_unique_port())
173185
.build()
174186
.expect("Failed to build StaticExporter");
187+
188+
// Always close the exporter to ensure proper release of WebDriver resources
189+
exporter.close();
175190
```
176191

177192
## Logging Support
@@ -190,6 +205,9 @@ env_logger::init();
190205
let mut exporter = StaticExporterBuilder::default()
191206
.build()
192207
.expect("Failed to create StaticExporter");
208+
209+
// Always close the exporter to ensure proper release of WebDriver resources
210+
exporter.close();
193211
```
194212

195213
## Performance Considerations

examples/customization/consistent_static_format_export/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ fn line_and_scatter_plot(
162162
info!(" - {file_name}.pdf");
163163
info!(" - {file_name}.svg");
164164
info!(" - {file_name}.png");
165+
166+
// Always close the exporter to ensure proper release of WebDriver resources
167+
exporter.close();
165168
}
166169

167170
fn read_from_file(file_path: &str) -> Vec<Vec<f64>> {

examples/static_export/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
2828
1.0,
2929
)?;
3030
plot3.write_image("./output/plot3_legacy_api", ImageFormat::SVG, 800, 600, 1.0)?;
31-
plot1.write_image("./output/plot3_legacy_api", ImageFormat::PDF, 800, 600, 1.0)?;
31+
32+
plot1.write_image("./output/plot1_legacy_api", ImageFormat::PDF, 800, 600, 1.0)?;
3233

3334
// Create a single StaticExporter to reuse across all plots
3435
// This is more efficient than creating a new exporter for each plot which
@@ -108,5 +109,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
108109
.expect("Failed to create custom StaticExporter");
109110
*/
110111

112+
// Always close the exporter to ensure proper release of WebDriver resources
113+
exporter.close();
114+
111115
Ok(())
112116
}

plotly/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ dyn-clone = "1"
5252
erased-serde = "0.4"
5353
image = { version = "0.25", optional = true }
5454
plotly_derive = { version = "0.13", path = "../plotly_derive" }
55-
plotly_static = { version = "0.0", path = "../plotly_static", optional = true }
55+
plotly_static = { version = "0.1", path = "../plotly_static", optional = true }
5656
plotly_kaleido = { version = "0.13", path = "../plotly_kaleido", optional = true }
5757
ndarray = { version = "0.16", optional = true }
5858
once_cell = "1"

plotly/src/layout/rangebreaks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use serde::Serialize;
44
use crate::private::NumOrString;
55

66
/// Struct representing a rangebreak for Plotly axes.
7-
/// See: https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-rangebreaks
7+
/// See: <https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-rangebreaks>
88
#[derive(Debug, Clone, Serialize, PartialEq, FieldSetter)]
99
pub struct RangeBreak {
1010
/// Sets the lower and upper bounds for this range break, e.g. ["sat",

plotly/src/layout/scene.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ impl Rotation {
357357
pub struct Projection {
358358
#[serde(rename = "type")]
359359
projection_type: Option<ProjectionType>,
360-
/// Sets the rotation of the map projection. See https://plotly.com/python/reference/layout/geo/#layout-geo-projection-rotation
360+
// Sets the rotation of the map projection. See https://plotly.com/python/reference/layout/geo/#layout-geo-projection-rotation
361361
#[serde(rename = "rotation")]
362362
rotation: Option<Rotation>,
363363
}

plotly/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//!
77
//! The `kaleido` and `kaleido_download` features are deprecated since version
88
//! 0.13.0 and will be removed in version 0.14.0. Please migrate to the
9-
//! `plotly_static` and `plotly_static_download` features instead.
9+
//! `plotly_static` and `static_export_*` features instead.
1010
#![recursion_limit = "256"] // lets us use a large serde_json::json! macro for testing crate::layout::Axis
1111
extern crate askama;
1212
extern crate rand;

plotly/src/plot.rs

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,10 @@ impl Plot {
532532
let mut exporter = plotly_static::StaticExporterBuilder::default()
533533
.build()
534534
.map_err(|e| format!("Failed to create StaticExporter: {e}"))?;
535-
self.write_image_with_exporter(&mut exporter, filename, format, width, height, scale)
535+
let result =
536+
self.write_image_with_exporter(&mut exporter, filename, format, width, height, scale);
537+
exporter.close();
538+
result
536539
}
537540

538541
/// Convert the `Plot` to a static image and return the image as a `base64`
@@ -560,7 +563,9 @@ impl Plot {
560563
let mut exporter = plotly_static::StaticExporterBuilder::default()
561564
.build()
562565
.map_err(|e| format!("Failed to create StaticExporter: {e}"))?;
563-
self.to_base64_with_exporter(&mut exporter, format, width, height, scale)
566+
let result = self.to_base64_with_exporter(&mut exporter, format, width, height, scale);
567+
exporter.close();
568+
result
564569
}
565570

566571
/// Convert the `Plot` to SVG and return it as a String using plotly_static.
@@ -583,7 +588,9 @@ impl Plot {
583588
let mut exporter = plotly_static::StaticExporterBuilder::default()
584589
.build()
585590
.map_err(|e| format!("Failed to create StaticExporter: {e}"))?;
586-
self.to_svg_with_exporter(&mut exporter, width, height, scale)
591+
let result = self.to_svg_with_exporter(&mut exporter, width, height, scale);
592+
exporter.close();
593+
result
587594
}
588595

589596
/// Convert the `Plot` to a static image of the given image format and save
@@ -752,6 +759,99 @@ impl Plot {
752759
)
753760
}
754761

762+
/// Convert the `Plot` to a static image and save at the given location
763+
/// using a provided AsyncStaticExporter.
764+
///
765+
/// Similar to the sync version in [`Self::write_image_with_exporter`], but
766+
/// meant for async contexts.
767+
///
768+
/// This method allows you to reuse a AsyncStaticExporter instance across
769+
/// multiple plots, which is more efficient than creating a new one for
770+
/// each operation.
771+
///
772+
/// For more details see the [plotly_static documentation](https://docs.rs/plotly_static/).
773+
#[cfg(feature = "plotly_static")]
774+
pub async fn write_image_async<P: AsRef<Path>>(
775+
&self,
776+
exporter: &mut plotly_static::AsyncStaticExporter,
777+
filename: P,
778+
format: ImageFormat,
779+
width: usize,
780+
height: usize,
781+
scale: f64,
782+
) -> Result<(), Box<dyn std::error::Error>> {
783+
exporter
784+
.write_fig(
785+
filename.as_ref(),
786+
&serde_json::to_value(self)?,
787+
format,
788+
width,
789+
height,
790+
scale,
791+
)
792+
.await
793+
}
794+
795+
/// Convert the `Plot` to a static image and return the image as a `base64`
796+
/// String using a provided AsyncStaticExporter. Supported formats are
797+
/// [ImageFormat::JPEG], [ImageFormat::PNG] and [ImageFormat::WEBP].
798+
///
799+
/// Similar to the sync version in [`Self::to_base64_with_exporter`], but
800+
/// meant for async contexts.
801+
///
802+
/// For more details see the [plotly_static documentation](https://docs.rs/plotly_static/).
803+
#[cfg(feature = "plotly_static")]
804+
pub async fn to_base64_async(
805+
&self,
806+
exporter: &mut plotly_static::AsyncStaticExporter,
807+
format: ImageFormat,
808+
width: usize,
809+
height: usize,
810+
scale: f64,
811+
) -> Result<String, Box<dyn std::error::Error>> {
812+
match format {
813+
ImageFormat::JPEG | ImageFormat::PNG | ImageFormat::WEBP => {
814+
exporter.write_to_string(
815+
&serde_json::to_value(self)?,
816+
format,
817+
width,
818+
height,
819+
scale,
820+
)
821+
.await
822+
}
823+
_ => {
824+
Err(format!("Cannot generate base64 string for ImageFormat:{format}. Allowed formats are JPEG, PNG, WEBP").into())
825+
}
826+
}
827+
}
828+
829+
/// Convert the `Plot` to SVG and return it as a String using a provided
830+
/// AsyncStaticExporter.
831+
///
832+
/// Similar to the sync version in [`Self::to_svg_with_exporter`], but
833+
/// meant for async contexts.
834+
///
835+
/// For more details see the [plotly_static documentation](https://docs.rs/plotly_static/).
836+
#[cfg(feature = "plotly_static")]
837+
pub async fn to_svg_async(
838+
&self,
839+
exporter: &mut plotly_static::AsyncStaticExporter,
840+
width: usize,
841+
height: usize,
842+
scale: f64,
843+
) -> Result<String, Box<dyn std::error::Error>> {
844+
exporter
845+
.write_to_string(
846+
&serde_json::to_value(self)?,
847+
ImageFormat::SVG,
848+
width,
849+
height,
850+
scale,
851+
)
852+
.await
853+
}
854+
755855
fn render(&self) -> String {
756856
let tmpl = PlotTemplate {
757857
plot: self,
@@ -900,7 +1000,6 @@ impl PartialEq for Plot {
9001000
#[cfg(test)]
9011001
mod tests {
9021002
use std::path::PathBuf;
903-
use std::sync::atomic::{AtomicU32, Ordering};
9041003

9051004
#[cfg(feature = "kaleido")]
9061005
use plotly_kaleido::ImageFormat;
@@ -1068,7 +1167,8 @@ mod tests {
10681167
// Helper to generate unique ports for parallel tests
10691168
#[cfg(feature = "plotly_static")]
10701169
fn get_unique_port() -> u32 {
1071-
static PORT_COUNTER: AtomicU32 = AtomicU32::new(5544);
1170+
use std::sync::atomic::{AtomicU32, Ordering};
1171+
static PORT_COUNTER: AtomicU32 = AtomicU32::new(5144);
10721172
PORT_COUNTER.fetch_add(1, Ordering::SeqCst)
10731173
}
10741174

plotly_static/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "plotly_static"
3-
version = "0.0.4"
3+
version = "0.1.0"
44
description = "Export Plotly graphs to static images using WebDriver"
55
authors = ["Andrei Gherghescu [email protected]"]
66
license = "MIT"

plotly_static/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Add to your `Cargo.toml`:
5656

5757
```toml
5858
[dependencies]
59-
plotly_static = { version = "0.0.4", features = ["chromedriver", "webdriver_download"] }
59+
plotly_static = { version = "0.1", features = ["chromedriver", "webdriver_download"] }
6060
serde_json = "1.0"
6161
```
6262

0 commit comments

Comments
 (0)