Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ 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).

https://github.com/plotly/plotly.rs/pull/350

## [0.13.6] - 2025-xx-xx

### Fixed

- [[#348](https://github.com/plotly/plotly.rs/pull/348)] Fix Pie chart color setting

### Changed

- [[#350](https://github.com/plotly/plotly.rs/pull/350)] Add `plotly_static` `async` API

## [0.13.5] - 2025-07-31

### Fixed
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ let base64_data = plot.to_base64(ImageFormat::PNG, 800, 600, 1.0)?;
let svg_string = plot.to_svg(800, 600, 1.0)?;
```

**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/).
**Note:** This feature requires a WebDriver-compatible browser (Chrome or Firefox) as well as a Webdriver (chromedriver/geckodriver) to be available on the system.

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`.

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.

For further details see [`plotly_static` crate documentation](https://docs.rs/plotly_static/).

## Exporting Static Images with Kaleido (legacy)

Expand Down
44 changes: 37 additions & 7 deletions docs/book/src/fundamentals/static_image_export.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ plotly = { version = "0.13", features = ["static_export_chromedriver", "static_e
plotly = { version = "0.13", features = ["static_export_default"] }
```

> 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.

## Prerequisites

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

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

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

// Export multiple plots using the same exporter
plot1.write_image_with_exporter(&mut exporter, "plot1", ImageFormat::PNG, 800, 600, 1.0)
exporter.write_image(&plot1, "plot1", ImageFormat::PNG, 800, 600, 1.0)
.expect("Failed to export plot1");
plot2.write_image_with_exporter(&mut exporter, "plot2", ImageFormat::JPEG, 800, 600, 1.0)
exporter.write_image(&plot2, "plot2", ImageFormat::JPEG, 800, 600, 1.0)
.expect("Failed to export plot2");

// Always close the exporter to ensure proper release of WebDriver resources
exporter.close();
```

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

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

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

// Get SVG data (vector format, scalable)
let svg_data = plot.to_svg_with_exporter(&mut exporter, 400, 300, 1.0)
let svg_data = exporter.to_svg(&plot, 400, 300, 1.0)
.expect("Failed to export plot");

// Always close the exporter to ensure proper release of WebDriver resources
exporter.close();
```

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.

## Advanced Configuration

### Custom WebDriver Configuration
Expand All @@ -150,6 +162,10 @@ let mut exporter = StaticExporterBuilder::default()
])
.build()
.expect("Failed to create StaticExporter");

// Always close the exporter to ensure proper release of WebDriver resources
exporter.close();

```

### Parallel Usage
Expand All @@ -172,8 +188,19 @@ let mut exporter = StaticExporterBuilder::default()
.webdriver_port(get_unique_port())
.build()
.expect("Failed to build StaticExporter");

// Always close the exporter to ensure proper release of WebDriver resources
exporter.close();
```

### Async support

`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.

> Note: Both sync and async exporters are available whenever a `static_export_*` feature is enabled in `plotly`.

For more details check the [`plotly_static` API Documentation](https://docs.rs/plotly_static/)

## Logging Support

Enable logging for debugging and monitoring:
Expand All @@ -190,6 +217,9 @@ env_logger::init();
let mut exporter = StaticExporterBuilder::default()
.build()
.expect("Failed to create StaticExporter");

// Always close the exporter to ensure proper release of WebDriver resources
exporter.close();
```

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

## Complete Example

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

- Multiple export formats
- Exporter reuse
Expand Down
13 changes: 10 additions & 3 deletions examples/customization/consistent_static_format_export/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use plotly::color::{NamedColor, Rgb};
use plotly::common::{Anchor, Font, Line, Marker, MarkerSymbol, Mode, Title};
use plotly::layout::{Axis, ItemSizing, Legend, Margin, Shape, ShapeLine, ShapeType};
use plotly::plotly_static::{ImageFormat, StaticExporterBuilder};
use plotly::prelude::*;
use plotly::{Layout, Plot, Scatter};

fn line_and_scatter_plot(
Expand Down Expand Up @@ -149,19 +150,25 @@ fn line_and_scatter_plot(
.unwrap();

info!("Exporting to PNG format...");
plot.write_image_with_exporter(&mut exporter, file_name, ImageFormat::PNG, 1280, 960, 1.0)
exporter
.write_image(&plot, file_name, ImageFormat::PNG, 1280, 960, 1.0)
.unwrap();
info!("Exporting to SVG format...");
plot.write_image_with_exporter(&mut exporter, file_name, ImageFormat::SVG, 1280, 960, 1.0)
exporter
.write_image(&plot, file_name, ImageFormat::SVG, 1280, 960, 1.0)
.unwrap();
info!("Exporting to PDF format...");
plot.write_image_with_exporter(&mut exporter, file_name, ImageFormat::PDF, 1280, 960, 1.0)
exporter
.write_image(&plot, file_name, ImageFormat::PDF, 1280, 960, 1.0)
.unwrap();

info!("Export complete. Check the output files:");
info!(" - {file_name}.pdf");
info!(" - {file_name}.svg");
info!(" - {file_name}.png");

// Always close the exporter to ensure proper release of WebDriver resources
exporter.close();
}

fn read_from_file(file_path: &str) -> Vec<Vec<f64>> {
Expand Down
6 changes: 4 additions & 2 deletions examples/static_export/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ authors = ["Andrei Gherghescu andrei-ng@protonmail.com"]
edition = "2021"
description = "Example demonstrating static image export using plotly_static with WebDriver"
readme = "README.md"
default-run = "sync"

[dependencies]
plotly = { path = "../../plotly", features = ["static_export_default"] }
env_logger = "0.10"
log = "0.4"
env_logger = "0.11"
log = "0.4"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
27 changes: 21 additions & 6 deletions examples/static_export/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ The `plotly_static` provides a interface for converting Plotly plots into variou

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.

See also the `Static Image Export` section in the book for a more detailed description.
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.

## Overview


## Features

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

## Running the Example
## Running the Example(s)

To run the `sync` API example

```bash
# Basic run
cargo run --bin sync

# With debug logging
RUST_LOG=debug cargo run --bin sync

# With custom WebDriver path
WEBDRIVER_PATH=/path/to/chromedriver cargo run --bin sync
```

To run the `async` API example

```bash
# Basic run
cargo run
cargo run --bin async

# With debug logging
RUST_LOG=debug cargo run
RUST_LOG=debug cargo run --bin async

# With custom WebDriver path
WEBDRIVER_PATH=/path/to/chromedriver cargo run
WEBDRIVER_PATH=/path/to/chromedriver cargo run --bin async
```

## Output
Expand Down
78 changes: 78 additions & 0 deletions examples/static_export/src/bin/async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use log::info;
use plotly::plotly_static::{ImageFormat, StaticExporterBuilder};
use plotly::prelude::*;
use plotly::{Plot, Scatter};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();

// Create some plots
let mut plot1 = Plot::new();
plot1.add_trace(Scatter::new(vec![1, 2, 3, 4], vec![10, 15, 13, 17]).name("trace1"));

let mut plot2 = Plot::new();
plot2.add_trace(Scatter::new(vec![2, 3, 4, 5], vec![16, 5, 11, 9]).name("trace2"));

std::fs::create_dir_all("./output").unwrap();

info!("Creating AsyncStaticExporter with default configuration...");
let mut exporter = StaticExporterBuilder::default()
.webdriver_port(5111)
.build_async()
.expect("Failed to create AsyncStaticExporter");

info!("Exporting multiple plots using a single AsyncStaticExporter...");
exporter
.write_image(
&plot1,
"./output/plot1_async_api",
ImageFormat::PNG,
800,
600,
1.0,
)
.await?;
exporter
.write_image(
&plot1,
"./output/plot1_async_api",
ImageFormat::JPEG,
800,
600,
1.0,
)
.await?;
exporter
.write_image(
&plot2,
"./output/plot2_async_api",
ImageFormat::SVG,
800,
600,
1.0,
)
.await?;
exporter
.write_image(
&plot2,
"./output/plot2_async_api",
ImageFormat::PDF,
800,
600,
1.0,
)
.await?;

info!("Exporting to base64 and SVG strings with async API...");
let _base64_data = exporter
.to_base64(&plot1, ImageFormat::PNG, 400, 300, 1.0)
.await?;
let _svg_data = exporter.to_svg(&plot1, 400, 300, 1.0).await?;

// Always close the exporter to ensure proper release of WebDriver resources
exporter.close().await;

info!("Async exports completed successfully!");
Ok(())
}
Loading
Loading