Skip to content

Commit fd36405

Browse files
committed
expose an async API for plotly_static
Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com>
1 parent ef25443 commit fd36405

File tree

19 files changed

+1093
-374
lines changed

19 files changed

+1093
-374
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file.
33

44
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).
55

6+
https://github.com/plotly/plotly.rs/pull/350
7+
8+
## [0.13.6] - 2025-xx-xx
9+
10+
### Fixed
11+
12+
- [[#348](https://github.com/plotly/plotly.rs/pull/348)] Fix Pie chart color setting
13+
14+
### Changed
15+
16+
- [[#350](https://github.com/plotly/plotly.rs/pull/350)] Add `plotly_static` `async` API
17+
618
## [0.13.5] - 2025-07-31
719

820
### Fixed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,13 @@ let base64_data = plot.to_base64(ImageFormat::PNG, 800, 600, 1.0)?;
119119
let svg_string = plot.to_svg(800, 600, 1.0)?;
120120
```
121121

122-
**Note:** This feature requires a WebDriver-compatible browser (Chrome or Firefox) as well as a Webdriver (chromedriver/geckodriver) to be available on the system. For advanced usage, see the [`plotly_static` crate documentation](https://docs.rs/plotly_static/).
122+
**Note:** This feature requires a WebDriver-compatible browser (Chrome or Firefox) as well as a Webdriver (chromedriver/geckodriver) to be available on the system.
123+
124+
The above example uses the legacy API that is backwards compatible with the Kaleido API. However, for more efficient workflows a `StaticExporter` object can be built and reused between calls to `write_image`.
125+
126+
More specificallt, enabling any of the `plotly` features `static_export_chromedriver`, `static_export_geckodriver`, or `static_export_default` gives access to both the synchronous `StaticExporter` and the asynchronous `AsyncStaticExporter` (available via `plotly::plotly_static`). For exporter reuse and up-to-date sync/async usage patterns, please see the dedicated example in `examples/static_export`, which demonstrates both synchronous and asynchronous exporters and how to reuse a single exporter instance across multiple exports.
127+
128+
For further details see [`plotly_static` crate documentation](https://docs.rs/plotly_static/).
123129

124130
## Exporting Static Images with Kaleido (legacy)
125131

docs/book/src/fundamentals/static_image_export.md

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ plotly = { version = "0.13", features = ["static_export_chromedriver", "static_e
3434
plotly = { version = "0.13", features = ["static_export_default"] }
3535
```
3636

37+
> Enabling any of the static export features in `plotly` (`static_export_chromedriver`, `static_export_geckodriver`, or `static_export_default`) enables both APIs from `plotly_static`: the sync `StaticExporter` and the async `AsyncStaticExporter` (reachable as `plotly::plotly_static::AsyncStaticExporter`). Prefer the async API inside async code.
38+
3739
## Prerequisites
3840

3941
1. **WebDriver Installation**: You need either chromedriver or geckodriver installed
40-
- Chrome: Download from https://chromedriver.chromium.org/
41-
- Firefox: Download from https://github.com/mozilla/geckodriver/releases
42+
- Chrome: Download from [https://chromedriver.chromium.org/](https://chromedriver.chromium.org/)
43+
- Firefox: Download from [https://github.com/mozilla/geckodriver/releases](https://github.com/mozilla/geckodriver/releases)
4244
- Or use the `static_export_wd_download` feature for automatic download
4345

4446
2. **Browser Installation**: You need Chrome/Chromium or Firefox installed
@@ -74,6 +76,7 @@ For better performance when exporting multiple plots, reuse a single `StaticExpo
7476
```rust
7577
use plotly::{Plot, Scatter};
7678
use plotly::plotly_static::{StaticExporterBuilder, ImageFormat};
79+
use plotly::prelude::*;
7780

7881
let mut plot1 = Plot::new();
7982
plot1.add_trace(Scatter::new(vec![1, 2, 3], vec![4, 5, 6]));
@@ -87,10 +90,13 @@ let mut exporter = StaticExporterBuilder::default()
8790
.expect("Failed to create StaticExporter");
8891

8992
// Export multiple plots using the same exporter
90-
plot1.write_image_with_exporter(&mut exporter, "plot1", ImageFormat::PNG, 800, 600, 1.0)
93+
exporter.write_image(&plot1, "plot1", ImageFormat::PNG, 800, 600, 1.0)
9194
.expect("Failed to export plot1");
92-
plot2.write_image_with_exporter(&mut exporter, "plot2", ImageFormat::JPEG, 800, 600, 1.0)
95+
exporter.write_image(&plot2, "plot2", ImageFormat::JPEG, 800, 600, 1.0)
9396
.expect("Failed to export plot2");
97+
98+
// Always close the exporter to ensure proper release of WebDriver resources
99+
exporter.close();
94100
```
95101

96102
## Supported Formats
@@ -114,6 +120,7 @@ For web applications or APIs, you can export to strings:
114120
```rust
115121
use plotly::{Plot, Scatter};
116122
use plotly::plotly_static::{StaticExporterBuilder, ImageFormat};
123+
use plotly::prelude::*;
117124

118125
let mut plot = Plot::new();
119126
plot.add_trace(Scatter::new(vec![1, 2, 3], vec![4, 5, 6]));
@@ -123,14 +130,19 @@ let mut exporter = StaticExporterBuilder::default()
123130
.expect("Failed to create StaticExporter");
124131

125132
// Get base64 data (useful for embedding in HTML)
126-
let base64_data = plot.to_base64_with_exporter(&mut exporter, ImageFormat::PNG, 400, 300, 1.0)
133+
let base64_data = exporter.to_base64(&plot, ImageFormat::PNG, 400, 300, 1.0)
127134
.expect("Failed to export plot");
128135

129136
// Get SVG data (vector format, scalable)
130-
let svg_data = plot.to_svg_with_exporter(&mut exporter, 400, 300, 1.0)
137+
let svg_data = exporter.to_svg(&plot, 400, 300, 1.0)
131138
.expect("Failed to export plot");
139+
140+
// Always close the exporter to ensure proper release of WebDriver resources
141+
exporter.close();
132142
```
133143

144+
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.
145+
134146
## Advanced Configuration
135147

136148
### Custom WebDriver Configuration
@@ -150,6 +162,10 @@ let mut exporter = StaticExporterBuilder::default()
150162
])
151163
.build()
152164
.expect("Failed to create StaticExporter");
165+
166+
// Always close the exporter to ensure proper release of WebDriver resources
167+
exporter.close();
168+
153169
```
154170

155171
### Parallel Usage
@@ -172,8 +188,19 @@ let mut exporter = StaticExporterBuilder::default()
172188
.webdriver_port(get_unique_port())
173189
.build()
174190
.expect("Failed to build StaticExporter");
191+
192+
// Always close the exporter to ensure proper release of WebDriver resources
193+
exporter.close();
175194
```
176195

196+
### Async support
197+
198+
`plotly_static` package offers an `async` API which is exposed in `plotly` via the `write_image_async`, `to_base64_async` and `to_svg_async` functions. However, the user must pass an `AsyncStaticExporter` asynchronous exporter instead of a synchronous one by building it via `StaticExportBuilder`'s `build_async` method.
199+
200+
> Note: Both sync and async exporters are available whenever a `static_export_*` feature is enabled in `plotly`.
201+
202+
For more details check the [`plotly_static` API Documentation](https://docs.rs/plotly_static/)
203+
177204
## Logging Support
178205

179206
Enable logging for debugging and monitoring:
@@ -190,6 +217,9 @@ env_logger::init();
190217
let mut exporter = StaticExporterBuilder::default()
191218
.build()
192219
.expect("Failed to create StaticExporter");
220+
221+
// Always close the exporter to ensure proper release of WebDriver resources
222+
exporter.close();
193223
```
194224

195225
## Performance Considerations
@@ -200,7 +230,7 @@ let mut exporter = StaticExporterBuilder::default()
200230

201231
## Complete Example
202232

203-
See the [static export example](../../../examples/static_export/) for a complete working example that demonstrates:
233+
See the [static export example](https://github.com/plotly/plotly.rs/tree/main/examples/static_export) for a complete working example that demonstrates:
204234

205235
- Multiple export formats
206236
- Exporter reuse

examples/customization/consistent_static_format_export/src/main.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use plotly::color::{NamedColor, Rgb};
33
use plotly::common::{Anchor, Font, Line, Marker, MarkerSymbol, Mode, Title};
44
use plotly::layout::{Axis, ItemSizing, Legend, Margin, Shape, ShapeLine, ShapeType};
55
use plotly::plotly_static::{ImageFormat, StaticExporterBuilder};
6+
use plotly::prelude::*;
67
use plotly::{Layout, Plot, Scatter};
78

89
fn line_and_scatter_plot(
@@ -149,19 +150,25 @@ fn line_and_scatter_plot(
149150
.unwrap();
150151

151152
info!("Exporting to PNG format...");
152-
plot.write_image_with_exporter(&mut exporter, file_name, ImageFormat::PNG, 1280, 960, 1.0)
153+
exporter
154+
.write_image(&plot, file_name, ImageFormat::PNG, 1280, 960, 1.0)
153155
.unwrap();
154156
info!("Exporting to SVG format...");
155-
plot.write_image_with_exporter(&mut exporter, file_name, ImageFormat::SVG, 1280, 960, 1.0)
157+
exporter
158+
.write_image(&plot, file_name, ImageFormat::SVG, 1280, 960, 1.0)
156159
.unwrap();
157160
info!("Exporting to PDF format...");
158-
plot.write_image_with_exporter(&mut exporter, file_name, ImageFormat::PDF, 1280, 960, 1.0)
161+
exporter
162+
.write_image(&plot, file_name, ImageFormat::PDF, 1280, 960, 1.0)
159163
.unwrap();
160164

161165
info!("Export complete. Check the output files:");
162166
info!(" - {file_name}.pdf");
163167
info!(" - {file_name}.svg");
164168
info!(" - {file_name}.png");
169+
170+
// Always close the exporter to ensure proper release of WebDriver resources
171+
exporter.close();
165172
}
166173

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

examples/static_export/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ authors = ["Andrei Gherghescu andrei-ng@protonmail.com"]
55
edition = "2021"
66
description = "Example demonstrating static image export using plotly_static with WebDriver"
77
readme = "README.md"
8+
default-run = "sync"
89

910
[dependencies]
1011
plotly = { path = "../../plotly", features = ["static_export_default"] }
11-
env_logger = "0.10"
12-
log = "0.4"
12+
env_logger = "0.11"
13+
log = "0.4"
14+
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

examples/static_export/README.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ The `plotly_static` provides a interface for converting Plotly plots into variou
66

77
In this example it is shown how to use the `StaticExporter` with the old style Kaleido API and also with the new style API. Using the former API is fine for one time static exports, but that API will crate an instance of the `StaticExporter` for each `write_image` call. The new style API is recommended for performance as the same instance of the `StaticExporter` can be reused across multiple exports.
88

9-
See also the `Static Image Export` section in the book for a more detailed description.
9+
When any of the `plotly` static export features are enabled (`static_export_chromedriver`, `static_export_geckodriver`, or `static_export_default`), both `StaticExporter` (sync) and `AsyncStaticExporter` (async) are available via `plotly::plotly_static`. This example includes separate `sync` and `async` bins demonstrating both. Refer to the [`plotly_static` API Documentation](https://docs.rs/plotly_static/) a more detailed description.
1010

1111
## Overview
1212

13-
1413
## Features
1514

15+
- **Async/Sync API**
1616
- **Multiple Export Formats**: PNG, JPEG, SVG, PDF
1717
- **Exporter Reuse (new API)**: Efficient reuse of a single `StaticExporter` instance
1818
- **String Export**: Base64 and SVG string output for web applications
@@ -45,17 +45,32 @@ plotly = { version = "0.13", features = ["static_export_geckodriver"] }
4545
plotly = { version = "0.13", features = ["static_export_chromedriver"] }
4646
```
4747

48-
## Running the Example
48+
## Running the Example(s)
49+
50+
To run the `sync` API example
51+
52+
```bash
53+
# Basic run
54+
cargo run --bin sync
55+
56+
# With debug logging
57+
RUST_LOG=debug cargo run --bin sync
58+
59+
# With custom WebDriver path
60+
WEBDRIVER_PATH=/path/to/chromedriver cargo run --bin sync
61+
```
62+
63+
To run the `async` API example
4964

5065
```bash
5166
# Basic run
52-
cargo run
67+
cargo run --bin async
5368

5469
# With debug logging
55-
RUST_LOG=debug cargo run
70+
RUST_LOG=debug cargo run --bin async
5671

5772
# With custom WebDriver path
58-
WEBDRIVER_PATH=/path/to/chromedriver cargo run
73+
WEBDRIVER_PATH=/path/to/chromedriver cargo run --bin async
5974
```
6075

6176
## Output
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use log::info;
2+
use plotly::plotly_static::{ImageFormat, StaticExporterBuilder};
3+
use plotly::prelude::*;
4+
use plotly::{Plot, Scatter};
5+
6+
#[tokio::main]
7+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
8+
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
9+
10+
// Create some plots
11+
let mut plot1 = Plot::new();
12+
plot1.add_trace(Scatter::new(vec![1, 2, 3, 4], vec![10, 15, 13, 17]).name("trace1"));
13+
14+
let mut plot2 = Plot::new();
15+
plot2.add_trace(Scatter::new(vec![2, 3, 4, 5], vec![16, 5, 11, 9]).name("trace2"));
16+
17+
std::fs::create_dir_all("./output").unwrap();
18+
19+
info!("Creating AsyncStaticExporter with default configuration...");
20+
let mut exporter = StaticExporterBuilder::default()
21+
.webdriver_port(5111)
22+
.build_async()
23+
.expect("Failed to create AsyncStaticExporter");
24+
25+
info!("Exporting multiple plots using a single AsyncStaticExporter...");
26+
exporter
27+
.write_image(
28+
&plot1,
29+
"./output/plot1_async_api",
30+
ImageFormat::PNG,
31+
800,
32+
600,
33+
1.0,
34+
)
35+
.await?;
36+
exporter
37+
.write_image(
38+
&plot1,
39+
"./output/plot1_async_api",
40+
ImageFormat::JPEG,
41+
800,
42+
600,
43+
1.0,
44+
)
45+
.await?;
46+
exporter
47+
.write_image(
48+
&plot2,
49+
"./output/plot2_async_api",
50+
ImageFormat::SVG,
51+
800,
52+
600,
53+
1.0,
54+
)
55+
.await?;
56+
exporter
57+
.write_image(
58+
&plot2,
59+
"./output/plot2_async_api",
60+
ImageFormat::PDF,
61+
800,
62+
600,
63+
1.0,
64+
)
65+
.await?;
66+
67+
info!("Exporting to base64 and SVG strings with async API...");
68+
let _base64_data = exporter
69+
.to_base64(&plot1, ImageFormat::PNG, 400, 300, 1.0)
70+
.await?;
71+
let _svg_data = exporter.to_svg(&plot1, 400, 300, 1.0).await?;
72+
73+
// Always close the exporter to ensure proper release of WebDriver resources
74+
exporter.close().await;
75+
76+
info!("Async exports completed successfully!");
77+
Ok(())
78+
}

0 commit comments

Comments
 (0)