Skip to content

Commit 3f29156

Browse files
committed
improve documentation and add example for static_export
Signed-off-by: Andrei Gherghescu <[email protected]>
1 parent 6ab848a commit 3f29156

File tree

16 files changed

+689
-113
lines changed

16 files changed

+689
-113
lines changed

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,32 @@ When the applications developed with `plotly.rs` are intended for other targets
108108

109109
Kaleido binaries are available on Github [release page](https://github.com/plotly/Kaleido/releases). It currently supports Linux(`x86_64`), Windows(`x86_64`) and MacOS(`x86_64`/`aarch64`).
110110

111-
## Exporting a Static Images
111+
## Exporting Static Images with plotly_static (Recommended)
112+
113+
The recommended way to export static images is using the `plotly_static` backend, which uses a headless browser via WebDriver (Chrome or Firefox) for rendering. This is available via the `static_export_default` feature:
114+
115+
```toml
116+
[dependencies]
117+
plotly = { version = "0.12", features = ["static_export_default"] }
118+
```
119+
120+
This supports PNG, JPEG, WEBP, SVG, and PDF formats:
121+
122+
```rust
123+
use plotly::{Plot, Scatter, ImageFormat};
124+
125+
let mut plot = Plot::new();
126+
plot.add_trace(Scatter::new(vec![0, 1, 2], vec![2, 1, 0]));
127+
128+
plot.write_image("out.png", ImageFormat::PNG, 800, 600, 1.0)?;
129+
plot.write_image("out.svg", ImageFormat::SVG, 800, 600, 1.0)?;
130+
let base64_data = plot.to_base64(ImageFormat::PNG, 800, 600, 1.0)?;
131+
let svg_string = plot.to_svg(800, 600, 1.0)?;
132+
```
133+
134+
**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/).
135+
136+
## Exporting Static Images with Kaleido (to be deprecated)
112137

113138
Enable the `kaleido` feature and opt in for automatic downloading of the `kaleido` binaries by doing the following
114139

docs/book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [ndarray Support](./fundamentals/ndarray_support.md)
88
- [Shapes](./fundamentals/shapes.md)
99
- [Themes](./fundamentals/themes.md)
10+
- [Static Image Export](./fundamentals/static_image_export.md)
1011
- [Recipes](./recipes.md)
1112
- [Basic Charts](./recipes/basic_charts.md)
1213
- [Scatter Plots](./recipes/basic_charts/scatter_plots.md)

docs/book/src/fundamentals.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,12 @@
1818

1919
# Fundamentals
2020

21-
Functionality that applies to the library as a whole is described in the next sections.
21+
Functionality that applies to the library as a whole is described in the next sections.
22+
23+
## Core Features
24+
25+
- **[Jupyter Support](./fundamentals/jupyter_support.md)**: Interactive plotting in Jupyter notebooks
26+
- **[ndarray Support](./fundamentals/ndarray_support.md)**: Integration with the ndarray crate for numerical computing
27+
- **[Shapes](./fundamentals/shapes.md)**: Adding shapes and annotations to plots
28+
- **[Themes](./fundamentals/themes.md)**: Customizing plot appearance with themes
29+
- **[Static Image Export](./fundamentals/static_image_export.md)**: Exporting plots to static images (PNG, JPEG, SVG, PDF) using WebDriver
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,226 @@
11
# Static Image Export
2+
3+
The `plotly` crate provides static image export functionality through the `plotly_static` crate, which uses WebDriver and headless browsers to render plots as static images.
4+
5+
## Overview
6+
7+
Static image export allows you to convert Plotly plots into various image formats (PNG, JPEG, WEBP, SVG, PDF) for use in reports, web applications, or any scenario where you need static images.
8+
9+
## Feature Flags
10+
11+
The static export functionality is controlled by feature flags in the main `plotly` crate:
12+
13+
### Required Features (choose one):
14+
- `static_export_chromedriver`: Uses Chrome/Chromium for rendering (requires chromedriver)
15+
- `static_export_geckodriver`: Uses Firefox for rendering (requires geckodriver)
16+
17+
### Optional Features:
18+
- `static_export_downloader`: Automatically downloads WebDriver binaries at build time
19+
- `static_export_default`: Convenience feature that includes geckodriver + downloader
20+
21+
### Cargo.toml Configuration Examples:
22+
23+
```toml
24+
# Basic usage with manual WebDriver installation
25+
[dependencies]
26+
plotly = { version = "0.12", features = ["static_export_chromedriver"] }
27+
28+
# With automatic WebDriver download
29+
[dependencies]
30+
plotly = { version = "0.12", features = ["static_export_chromedriver", "static_export_downloader"] }
31+
32+
# Recommended: Default configuration with Firefox + auto-download
33+
[dependencies]
34+
plotly = { version = "0.12", features = ["static_export_default"] }
35+
```
36+
37+
## Prerequisites
38+
39+
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+
- Or use the `static_export_downloader` feature for automatic download
43+
44+
2. **Browser Installation**: You need Chrome/Chromium or Firefox installed
45+
46+
3. **Environment Variable** (optional): Set `WEBDRIVER_PATH` to specify custom WebDriver location
47+
```bash
48+
export WEBDRIVER_PATH=/path/to/chromedriver
49+
```
50+
51+
## Basic Usage
52+
53+
### Simple Export
54+
55+
```rust
56+
use plotly::{Plot, Scatter};
57+
use plotly::plotly_static::ImageFormat;
58+
59+
let mut plot = Plot::new();
60+
plot.add_trace(Scatter::new(vec![1, 2, 3], vec![4, 5, 6]));
61+
62+
// Export to PNG file
63+
plot.write_image("my_plot", ImageFormat::PNG, 800, 600, 1.0)
64+
.expect("Failed to export plot");
65+
```
66+
67+
### Efficient Exporter Reuse
68+
69+
For better performance when exporting multiple plots, reuse a single `StaticExporter`:
70+
71+
```rust
72+
use plotly::{Plot, Scatter};
73+
use plotly::plotly_static::{StaticExporterBuilder, ImageFormat};
74+
75+
let mut plot1 = Plot::new();
76+
plot1.add_trace(Scatter::new(vec![1, 2, 3], vec![4, 5, 6]));
77+
78+
let mut plot2 = Plot::new();
79+
plot2.add_trace(Scatter::new(vec![2, 3, 4], vec![5, 6, 7]));
80+
81+
// Create a single exporter to reuse
82+
let mut exporter = StaticExporterBuilder::default()
83+
.build()
84+
.expect("Failed to create StaticExporter");
85+
86+
// Export multiple plots using the same exporter
87+
plot1.write_image_with_exporter(&mut exporter, "plot1", ImageFormat::PNG, 800, 600, 1.0)
88+
.expect("Failed to export plot1");
89+
plot2.write_image_with_exporter(&mut exporter, "plot2", ImageFormat::JPEG, 800, 600, 1.0)
90+
.expect("Failed to export plot2");
91+
```
92+
93+
## Supported Formats
94+
95+
### Raster Formats
96+
- **PNG**: Portable Network Graphics, lossless compression
97+
- **JPEG**: Joint Photographic Experts Group, lossy compression (smaller files)
98+
- **WEBP**: Google's image format
99+
100+
### Vector Formats
101+
- **SVG**: Scalable Vector Graphics
102+
- **PDF**: Portable Document Format
103+
104+
### Deprecated
105+
- **EPS**: Encapsulated PostScript (will be removed in version 0.14.0)
106+
107+
## String Export
108+
109+
For web applications or APIs, you can export to strings:
110+
111+
```rust
112+
use plotly::{Plot, Scatter};
113+
use plotly::plotly_static::{StaticExporterBuilder, ImageFormat};
114+
115+
let mut plot = Plot::new();
116+
plot.add_trace(Scatter::new(vec![1, 2, 3], vec![4, 5, 6]));
117+
118+
let mut exporter = StaticExporterBuilder::default()
119+
.build()
120+
.expect("Failed to create StaticExporter");
121+
122+
// Get base64 data (useful for embedding in HTML)
123+
let base64_data = plot.to_base64_with_exporter(&mut exporter, ImageFormat::PNG, 400, 300, 1.0)
124+
.expect("Failed to export plot");
125+
126+
// Get SVG data (vector format, scalable)
127+
let svg_data = plot.to_svg_with_exporter(&mut exporter, 400, 300, 1.0)
128+
.expect("Failed to export plot");
129+
```
130+
131+
## Advanced Configuration
132+
133+
### Custom WebDriver Configuration
134+
135+
```rust
136+
use plotly::plotly_static::StaticExporterBuilder;
137+
138+
let mut exporter = StaticExporterBuilder::default()
139+
.webdriver_port(4445) // Use different port for parallel operations
140+
.spawn_webdriver(true) // Explicitly spawn WebDriver
141+
.offline_mode(true) // Use bundled JavaScript (no internet required)
142+
.webdriver_browser_caps(vec![
143+
"--headless".to_string(),
144+
"--no-sandbox".to_string(),
145+
"--disable-gpu".to_string(),
146+
"--disable-dev-shm-usage".to_string(),
147+
])
148+
.build()
149+
.expect("Failed to create StaticExporter");
150+
```
151+
152+
### Parallel Usage
153+
154+
For parallel operations (tests, etc.), use unique ports:
155+
156+
```rust
157+
use plotly::plotly_static::StaticExporterBuilder;
158+
use std::sync::atomic::{AtomicU32, Ordering};
159+
160+
// Generate unique ports for parallel usage
161+
static PORT_COUNTER: AtomicU32 = AtomicU32::new(4444);
162+
163+
fn get_unique_port() -> u32 {
164+
PORT_COUNTER.fetch_add(1, Ordering::SeqCst)
165+
}
166+
167+
// Each thread/process should use a unique port
168+
let mut exporter = StaticExporterBuilder::default()
169+
.webdriver_port(get_unique_port())
170+
.build()
171+
.expect("Failed to build StaticExporter");
172+
```
173+
174+
## Logging Support
175+
176+
Enable logging for debugging and monitoring:
177+
178+
```rust
179+
use plotly::plotly_static::StaticExporterBuilder;
180+
181+
// Initialize logging (typically done once at the start of your application)
182+
env_logger::init();
183+
184+
// Set log level via environment variable
185+
// RUST_LOG=debug cargo run
186+
187+
let mut exporter = StaticExporterBuilder::default()
188+
.build()
189+
.expect("Failed to create StaticExporter");
190+
```
191+
192+
## Performance Considerations
193+
194+
- **Exporter Reuse**: Create a single `StaticExporter` and reuse it for multiple plots
195+
- **Parallel Usage**: Use unique ports for parallel operations (tests, etc.)
196+
- **Resource Management**: The exporter automatically manages WebDriver lifecycle
197+
- **Format Selection**: Choose appropriate formats for your use case:
198+
- PNG: Good quality, lossless
199+
- JPEG: Smaller files, lossy
200+
- SVG: Scalable, good for web
201+
- PDF: Good for printing
202+
203+
## Complete Example
204+
205+
See the [static export example](../../../examples/static_export/) for a complete working example that demonstrates:
206+
207+
- Multiple export formats
208+
- Exporter reuse
209+
- String export
210+
- Logging
211+
- Error handling
212+
213+
To run the example:
214+
215+
```bash
216+
cd examples/static_export
217+
cargo run
218+
```
219+
**NOTE** Set `RUST_LOG=debug` to see detailed WebDriver operations and troubleshooting information.
220+
221+
## Related Documentation
222+
223+
- [plotly_static crate documentation](https://docs.rs/plotly_static/)
224+
- [WebDriver specification](https://w3c.github.io/webdriver/)
225+
- [GeckoDriver documentation](https://firefox-source-docs.mozilla.org/testing/geckodriver/)
226+
- [ChromeDriver documentation](https://chromedriver.chromium.org/)

examples/custom_controls/src/main.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ fn sinusoidal_slider_example(show: bool, file_name: &str) {
280280
Visible::False
281281
}) // Make 10th trace visible
282282
.line(plotly::common::Line::new().color("#00CED1").width(6.0))
283-
.name(format!("ν = {:.1}", frequency));
283+
.name(format!("ν = {frequency:.1}"));
284284
plot.add_trace(trace);
285285
}
286286

@@ -291,12 +291,11 @@ fn sinusoidal_slider_example(show: bool, file_name: &str) {
291291
let mut visible = vec![Visible::False; num_steps];
292292
visible[i] = Visible::True;
293293
let step = SliderStepBuilder::new()
294-
.label(format!("step-{}", i))
295-
.value(format!("{:.1}", frequency))
294+
.label(format!("step-{i}"))
295+
.value(format!("{frequency:.1}"))
296296
.push_restyle(Scatter::<f64, f64>::modify_visible(visible))
297297
.push_relayout(Layout::modify_title(format!(
298-
"Slider switched to step: {}",
299-
i
298+
"Slider switched to step: {i}"
300299
)))
301300
.build()
302301
.unwrap();
@@ -389,12 +388,11 @@ fn gdp_life_expectancy_slider_example(show: bool, file_name: &str) {
389388
visible[start..end].fill(Visible::True);
390389

391390
SliderStepBuilder::new()
392-
.label(format!("year = {}", year))
391+
.label(format!("year = {year}"))
393392
.value(year)
394393
.push_restyle(Scatter::<f64, f64>::modify_visible(visible))
395394
.push_relayout(Layout::modify_title(format!(
396-
"GDP vs. Life Expectancy ({})",
397-
year
395+
"GDP vs. Life Expectancy ({year})"
398396
)))
399397
.build()
400398
.unwrap()

examples/customization/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ fn multiple_plots_on_same_html_page(show: bool, file_name: &str) {
4949
.to_html_string();
5050

5151
std::fs::create_dir_all("./output").unwrap();
52-
let path = format!("./output/inline_{}.html", file_name);
52+
let path = format!("./output/inline_{file_name}.html");
5353
let mut file = File::create(&path).unwrap();
5454
file.write_all(html.as_bytes())
5555
.expect("failed to write html output");

examples/plotly_utils/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ pub fn write_example_to_html(plot: &Plot, name: &str) -> String {
1818
std::fs::create_dir_all("./output").unwrap();
1919
// Write inline HTML
2020
let html = plot.to_inline_html(Some(name));
21-
let path = format!("./output/inline_{}.html", name);
21+
let path = format!("./output/inline_{name}.html");
2222
std::fs::write(path, html).unwrap();
2323
// Write standalone HTML
24-
let path = format!("./output/{}.html", name);
24+
let path = format!("./output/{name}.html");
2525
plot.write_html(&path);
2626
path
2727
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ name = "static_export_example"
33
version = "0.1.0"
44
authors = ["Andrei Gherghescu [email protected]"]
55
edition = "2021"
6+
description = "Example demonstrating static image export using plotly_static with WebDriver"
7+
readme = "README.md"
68

79
[dependencies]
810
plotly = { path = "../../plotly", features = ["static_export_default"] }
911
env_logger = "0.10"
10-
log = "0.4"
12+
log = "0.4"

0 commit comments

Comments
 (0)