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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use crate::app::WkhtmlError;
use crate::app::{App, WkhtmlInput};
use std::collections::HashMap;
use std::env;
#[derive(Debug, Clone)]
pub enum ImgFormat {
Jpg,
Png,
Bmp,
Svg,
}
//display imgformat
impl std::fmt::Display for ImgFormat {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ImgFormat::Jpg => write!(f, "jpg"),
ImgFormat::Png => write!(f, "png"),
ImgFormat::Bmp => write!(f, "bmp"),
ImgFormat::Svg => write!(f, "svg"),
}
}
}
#[derive(Debug, Clone)]
pub struct ImgApp {
pub app: App,
pub options: HashMap<String, Option<String>>,
pub format: ImgFormat,
}
impl ImgApp {
pub fn new() -> Result<Self, WkhtmlError> {
let wkhtmltoimg_cmd =
env::var("WKHTMLTOIMG_CMD").unwrap_or_else(|_| "wkhtmltoimage".to_string());
Ok(Self {
app: App::new(wkhtmltoimg_cmd)?,
options: Self::default_options(),
format: ImgFormat::Jpg,
})
}
pub fn set_format(&mut self, format: ImgFormat) -> Result<&mut Self, WkhtmlError> {
let format = match format {
ImgFormat::Jpg => "jpg",
ImgFormat::Png => "png",
ImgFormat::Bmp => "bmp",
ImgFormat::Svg => "svg",
};
self.set_arg("format", Some(format.to_string()))?;
Ok(self)
}
fn build_args(&self) -> Vec<String> {
let mut args = Vec::new();
for (key, value) in &self.options {
match value {
Some(v) => {
if v != "false" {
if v == "true" {
if key == "toc" || key == "cover" {
args.push(key.to_string());
} else {
args.push(format!("--{}", key));
}
} else {
if key == "toc" || key == "cover" {
args.push(key.to_string());
} else {
args.push(format!("--{}", key));
args.push(format!("{}", v));
}
}
}
}
_ => {}
}
}
args
}
pub fn set_work_dir(&mut self, work_dir: &str) -> Result<&mut Self, WkhtmlError> {
self.app.set_work_dir(work_dir)?;
Ok(self)
}
pub fn set_args(
&mut self,
args: HashMap<String, Option<String>>,
) -> Result<&mut Self, WkhtmlError> {
for (key, value) in args {
self.set_arg(&key, value)?;
}
Ok(self)
}
pub fn set_arg(&mut self, key: &str, arg: Option<String>) -> Result<&mut Self, WkhtmlError> {
if self.options.contains_key(key) {
self.options.insert(key.to_string(), arg);
Ok(self)
} else {
Err(WkhtmlError::ServiceErr(format!("Invalid option: {}", key)))
}
}
pub fn run(&self, input: WkhtmlInput, name: &str) -> Result<String, WkhtmlError> {
let name = &format!("{}.{}", name, self.format.clone());
match input {
WkhtmlInput::File(path) => self.app.run_with_file(&path, name, self.build_args()),
WkhtmlInput::Url(url) => self.app.run_with_url(&url, name, self.build_args()),
WkhtmlInput::Html(html) => self.app.run_with_html(&html, name, self.build_args()),
}
}
pub fn default_extension() -> String {
"jpg".to_string()
}
fn default_options() -> HashMap<String, Option<String>> {
HashMap::from([
("allow".to_string(), None), // Allow the file or files from the specified folder to be loaded (repeatable)
("bypass-proxy-for".to_string(), None), // Bypass proxy for host (repeatable)
("cache-dir".to_string(), None), // Web cache directory
("checkbox-checked-svg".to_string(), None), // Use this SVG file when rendering checked checkboxes
("checked-svg".to_string(), None), // Use this SVG file when rendering unchecked checkboxes
("cookie".to_string(), None), // Set an additional cookie (repeatable)
("cookie-jar".to_string(), None), // Read and write cookies from and to the supplied cookie jar file
("crop-h".to_string(), None), // Set height for cropping
("crop-w".to_string(), None), // Set width for cropping
("crop-x".to_string(), None), // Set x coordinate for cropping (default 0)
("crop-y".to_string(), None), // Set y coordinate for cropping (default 0)
("custom-header".to_string(), None), // Set an additional HTTP header (repeatable)
("custom-header-propagation".to_string(), None), // Add HTTP headers specified by --custom-header for each resource request.
("no-custom-header-propagation".to_string(), None), // Do not add HTTP headers specified by --custom-header for each resource request.
("debug-javascript".to_string(), None), // Show javascript debugging output
("no-debug-javascript".to_string(), None), // Do not show javascript debugging output (default)
("encoding".to_string(), None), // Set the default text encoding, for input
("format".to_string(), Some(ImgApp::default_extension())), // Output format
("height".to_string(), None), // Set screen height (default is calculated from page content) (default 0)
("images".to_string(), None), // Do load or print images (default)
("no-images".to_string(), None), // Do not load or print images
("disable-javascript".to_string(), None), // Do not allow web pages to run javascript
("enable-javascript".to_string(), None), // Do allow web pages to run javascript (default)
("javascript-delay".to_string(), None), // Wait some milliseconds for javascript finish (default 200)
("load-error-handling".to_string(), None), // Specify how to handle pages that fail to load: abort, ignore or skip (default abort)
("load-media-error-handling".to_string(), None), // Specify how to handle media files that fail to load: abort, ignore or skip (default ignore)
("disable-local-file-access".to_string(), None), // Do not allowed conversion of a local file to read in other local files, unless explicitly allowed with allow
("enable-local-file-access".to_string(), None), // Allowed conversion of a local file to read in other local files. (default)
("minimum-font-size".to_string(), None), // Minimum font size
("password".to_string(), None), // HTTP Authentication password
("disable-plugins".to_string(), None), // Disable installed plugins (default)
("enable-plugins".to_string(), None), // Enable installed plugins (plugins will likely not work)
("post".to_string(), None), // Add an additional post field
("post-file".to_string(), None), // Post an additional file
("proxy".to_string(), None), // Use a proxy
("quality".to_string(), None), // Output image quality (between 0 and 100) (default 94)
("quiet".to_string(), None), // Be less verbose
("radiobutton-checked-svg".to_string(), None), // Use this SVG file when rendering checked radio-buttons
("radiobutton-svg".to_string(), None), // Use this SVG file when rendering unchecked radio-buttons
("run-script".to_string(), None), // Run this additional javascript after the page is done loading (repeatable)
("disable-smart-width".to_string(), None), // Use the specified width even if it is not large enough for the content
("enable-smart-width".to_string(), None), // Extend --width to fit unbreakable content (default)
("stop-slow-scripts".to_string(), None), // Stop slow running javascript
("no-stop-slow-scripts".to_string(), None), // Do not stop slow running javascript (default)
("transparent".to_string(), None), // Make the background transparent in pngs *
("use-xserver".to_string(), None), // Use the X server (some plugins and other stuff might not work without X11)
("user-style-sheet".to_string(), None), // Specify a user style sheet, to load with every page
("username".to_string(), None), // HTTP Authentication username
("width".to_string(), None), // Set screen width (default is 1024)
("window-status".to_string(), None), // Wait until window.status is equal to this string before rendering page
("zoom".to_string(), None), // Use this zoom factor (default 1)
])
}
}