-
Notifications
You must be signed in to change notification settings - Fork 124
Expand file tree
/
Copy pathgenerate_static.rs
More file actions
135 lines (115 loc) · 3.5 KB
/
generate_static.rs
File metadata and controls
135 lines (115 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::fs;
use std::io::{self, Read};
use std::path::PathBuf;
use clap::{Parser, ValueEnum};
use log::info;
use plotly_static::{ImageFormat, StaticExporterBuilder};
use serde_json::Value;
#[derive(Parser)]
#[command(name = "plotly-static-export")]
#[command(about = "Export Plotly plots to static images")]
#[command(version)]
struct Cli {
/// Input file containing Plotly JSON (use '-' for stdin)
#[arg(short, long, required = true, default_value = "-")]
input: String,
/// Output file path
#[arg(short, long, default_value = "output")]
output: PathBuf,
/// Image format
#[arg(short, long, value_enum, default_value_t = ImageFormatArg::PNG)]
format: ImageFormatArg,
/// Image width in pixels
#[arg(long, default_value_t = 800)]
width: usize,
/// Image height in pixels
#[arg(long, default_value_t = 600)]
height: usize,
/// Image scale factor
#[arg(short, long, default_value_t = 1.0)]
scale: f64,
/// Use offline mode (bundled JavaScript)
#[arg(long)]
offline: bool,
}
#[derive(ValueEnum, Clone)]
#[allow(clippy::upper_case_acronyms)]
enum ImageFormatArg {
PNG,
JPEG,
WEBP,
SVG,
PDF,
}
impl std::fmt::Display for ImageFormatArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ImageFormatArg::PNG => "PNG",
ImageFormatArg::JPEG => "JPEG",
ImageFormatArg::WEBP => "WEBP",
ImageFormatArg::SVG => "SVG",
ImageFormatArg::PDF => "PDF",
}
)
}
}
impl From<ImageFormatArg> for ImageFormat {
fn from(format: ImageFormatArg) -> Self {
match format {
ImageFormatArg::PNG => ImageFormat::PNG,
ImageFormatArg::JPEG => ImageFormat::JPEG,
ImageFormatArg::WEBP => ImageFormat::WEBP,
ImageFormatArg::SVG => ImageFormat::SVG,
ImageFormatArg::PDF => ImageFormat::PDF,
}
}
}
fn read_json_from_stdin() -> Result<Value, Box<dyn std::error::Error>> {
let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer)?;
let json: Value = serde_json::from_str(&buffer)?;
Ok(json)
}
fn read_json_from_file(path: &str) -> Result<Value, Box<dyn std::error::Error>> {
let content = fs::read_to_string(path)?;
let json: Value = serde_json::from_str(&content)?;
Ok(json)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let cli = Cli::parse();
// Read JSON input
let plot_json = if cli.input == "-" {
info!("Reading Plotly JSON from stdin...");
read_json_from_stdin()?
} else {
info!("Reading Plotly JSON from file: {}", cli.input);
read_json_from_file(&cli.input)?
};
// Validate that the JSON has the expected structure
if !plot_json.is_object() {
return Err("Invalid JSON: expected an object".into());
}
// Build StaticExporter instance
let mut exporter = StaticExporterBuilder::default()
.offline_mode(cli.offline)
.build()?;
info!(
"Exporting plot to {} format ({}x{} pixels, scale: {})...",
cli.format, cli.width, cli.height, cli.scale
);
// Export the plot
exporter.write_fig(
&cli.output,
&plot_json,
cli.format.into(),
cli.width,
cli.height,
cli.scale,
)?;
info!("Successfully exported plot to: {}", cli.output.display());
Ok(())
}