Skip to content

Commit a5b470e

Browse files
committed
refactor js templates
- add upload the pdf example result from the test to the ci for debugging Signed-off-by: Andrei Gherghescu <[email protected]>
1 parent 920066c commit a5b470e

File tree

4 files changed

+83
-70
lines changed

4 files changed

+83
-70
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ jobs:
129129
130130
cargo test --verbose --workspace --features plotly_ndarray,plotly_image,static_export_chromedriver --exclude plotly_kaleido
131131
132+
- name: Upload example.pdf artifact
133+
uses: actions/upload-artifact@v4
134+
with:
135+
name: example-pdf-${{ matrix.os }}
136+
path: ${{ github.workspace }}/plotly_static/example.pdf
137+
retention-days: 30
138+
132139
code-coverage:
133140
name: Code Coverage
134141
runs-on: ubuntu-latest

plotly_static/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ exclude = ["target/*"]
1616
webdriver_download = []
1717
geckodriver = []
1818
chromedriver = []
19+
html2pdf_export_timeout = []
1920

2021
[dependencies]
2122
log = "0.4"

plotly_static/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ use serde_json::map::Map as JsonMap;
274274
use urlencoding::encode;
275275
use webdriver::WebDriver;
276276

277-
use crate::template::{IMAGE_EXPORT_JS_SCRIPT, PDF_EXPORT_JS_SCRIPT};
277+
use crate::template::{image_export_js_script, pdf_export_js_script};
278278

279279
#[cfg(all(feature = "chromedriver", not(target_os = "windows")))]
280280
fn chrome_default_caps() -> Vec<&'static str> {
@@ -969,7 +969,7 @@ impl StaticExporter {
969969
plot.scale.into(),
970970
];
971971

972-
(PDF_EXPORT_JS_SCRIPT, args)
972+
(pdf_export_js_script(), args)
973973
}
974974
_ => {
975975
let args = vec![
@@ -980,11 +980,11 @@ impl StaticExporter {
980980
plot.scale.into(),
981981
];
982982

983-
(IMAGE_EXPORT_JS_SCRIPT, args)
983+
(image_export_js_script(), args)
984984
}
985985
};
986986

987-
let data = client.execute_async(js_script, args).await?;
987+
let data = client.execute_async(&js_script, args).await?;
988988

989989
// Don't close the client - keep it for reuse
990990
// client.close().await?;
@@ -1298,7 +1298,7 @@ mod tests {
12981298
assert!(dst.exists());
12991299
let metadata = std::fs::metadata(&dst).expect("Could not retrieve file metadata");
13001300
let file_size = metadata.len();
1301-
assert!(file_size > 0,);
1301+
assert!(file_size > 600000,);
13021302
// assert!(std::fs::remove_file(dst.as_path()).is_ok());
13031303
}
13041304

plotly_static/src/template.rs

Lines changed: 70 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use rand::{
1212
rng,
1313
};
1414

15-
pub(crate) const IMAGE_EXPORT_JS_SCRIPT: &str = r#"
15+
pub(crate) fn image_export_js_script() -> String {
16+
r#"
1617
const plot = arguments[0];
1718
const format = arguments[1];
1819
const width = arguments[2];
@@ -33,9 +34,25 @@ pub(crate) const IMAGE_EXPORT_JS_SCRIPT: &str = r#"
3334
console.error('Plotly error:', err);
3435
callback('ERROR:' + err.toString());
3536
});
36-
"#;
37+
"#
38+
.to_string()
39+
}
40+
41+
pub(crate) fn pdf_export_js_script() -> String {
42+
let timeout_ms = if cfg!(feature = "html2pdf_export_timeout") {
43+
500
44+
} else {
45+
250
46+
};
3747

38-
pub(crate) const PDF_EXPORT_JS_SCRIPT: &str = r##"
48+
let foreign_object_rendering = if cfg!(feature = "chromedriver") {
49+
"true"
50+
} else {
51+
"false"
52+
};
53+
54+
format!(
55+
r##"
3956
const plot = arguments[0];
4057
const format = arguments[1];
4158
const width = arguments[2];
@@ -50,45 +67,42 @@ pub(crate) const PDF_EXPORT_JS_SCRIPT: &str = r##"
5067
const graph_div = document.getElementById('plotly-html-element');
5168
5269
// Check if html2pdf is available
53-
if (typeof html2pdf === 'undefined') {
70+
if (typeof html2pdf === 'undefined') {{
5471
console.error('html2pdf library not available');
5572
callback('ERROR:html2pdf library not loaded');
5673
return;
57-
}
58-
59-
console.log('html2pdf library is available');
74+
}}
6075
61-
let tempDiv = null;
62-
let mmWidth, mmHeight;
76+
let tempDiv = null;
6377
64-
Plotly.newPlot(graph_div, plot).then(function() {
65-
console.log('Plotly plot created successfully');
78+
const cleanup = () => {{
79+
if (tempDiv) {{
80+
document.body.removeChild(tempDiv);
81+
}}
82+
}};
6683
67-
// Force PNG format for PDF export to test if SVG is the issue
68-
return Plotly.toImage(graph_div, {
69-
format: 'png',
84+
Plotly.newPlot(graph_div, plot).then(function() {{
85+
return Plotly.toImage(graph_div, {{
86+
format: format,
7087
width: width,
7188
height: height,
72-
});
73-
}).then(function(dataUrl) {
89+
}});
90+
}}).then(function(dataUrl) {{
7491
console.log('Plotly image generated successfully');
7592
console.log('SVG data URL length:', dataUrl.length);
7693
console.log('SVG data URL preview:', dataUrl.substring(0, 200) + '...');
77-
78-
// Convert px to mm: 1px = 0.264583 mm
79-
mmWidth = width * 0.264583;
80-
mmHeight = height * 0.264583;
81-
82-
console.log('PDF dimensions (mm):', mmWidth, 'x', mmHeight);
94+
console.log('PDF dimensions (px):', width, 'x', height);
8395
8496
// Create a temporary div for the image
8597
tempDiv = document.createElement('div');
8698
tempDiv.style.width = width + 'px';
8799
tempDiv.style.height = height + 'px';
88100
tempDiv.style.background = 'white';
89101
tempDiv.style.position = 'fixed';
90-
tempDiv.style.left = '0px';
91102
tempDiv.style.top = '0px';
103+
tempDiv.style.left = '0px';
104+
tempDiv.style.margin = '0px';
105+
tempDiv.style.padding = '0px';
92106
tempDiv.style.overflow = 'hidden';
93107
tempDiv.style.boxSizing = 'border-box';
94108
tempDiv.style.zIndex = '9999';
@@ -105,35 +119,30 @@ pub(crate) const PDF_EXPORT_JS_SCRIPT: &str = r##"
105119
img.style.objectFit = 'contain';
106120
img.style.maxWidth = '100%';
107121
img.style.maxHeight = '100%';
122+
img.style.verticalAlign = 'top';
123+
img.style.boxSizing = 'border-box';
108124
tempDiv.appendChild(img);
109125
110126
// Wait for the image to load
111-
return new Promise(function(resolve) {
112-
img.onload = function() {
127+
return new Promise(function(resolve) {{
128+
img.onload = function() {{
113129
console.log('SVG image loaded successfully');
114130
// Additional delay to ensure image is fully rendered
115-
setTimeout(function() {
116-
console.log('SVG rendering delay completed');
117-
resolve();
118-
}, 500);
119-
};
120-
img.onerror = function() {
121-
console.error('Failed to load SVG image');
122-
if (tempDiv) {
123-
document.body.removeChild(tempDiv);
124-
}
131+
// Brief delay to ensure image is fully rendered
132+
setTimeout(resolve, {timeout_ms});
133+
}};
134+
img.onerror = function() {{
135+
cleanup();
125136
callback('ERROR:Failed to load SVG image');
126-
};
127-
});
128-
}).then(function() {
137+
}};
138+
}});
139+
}}).then(function() {{
129140
console.log('Starting PDF generation...');
130-
131-
// Generate PDF with more robust configuration
132-
return html2pdf().from(tempDiv).set({
141+
return html2pdf().from(tempDiv).set({{
133142
margin: 0,
134143
filename: 'plotly-plot.pdf',
135-
image: { type: 'jpeg', quality: 1},
136-
html2canvas: {
144+
image: {{ type: 'jpeg', quality: 1}},
145+
html2canvas: {{
137146
scale: scale,
138147
backgroundColor: '#fff',
139148
useCORS: true,
@@ -143,31 +152,27 @@ pub(crate) const PDF_EXPORT_JS_SCRIPT: &str = r##"
143152
height: height,
144153
imageTimeout: 15000,
145154
removeContainer: true,
146-
foreignObjectRendering: true
147-
},
148-
jsPDF: {
149-
unit: 'mm',
150-
format: [mmWidth, mmHeight],
151-
orientation: mmWidth > mmHeight ? 'landscape' : 'portrait',
155+
foreignObjectRendering: {foreign_object_rendering},
156+
scrollY: 0,
157+
scrollX: 0
158+
}},
159+
jsPDF: {{
160+
unit: 'px',
161+
format: [width, height],
162+
orientation: width > height ? 'landscape' : 'portrait',
152163
compress: true
153-
}
154-
}).toPdf().output('datauristring');
155-
}).then(function(dataUri) {
156-
console.log('PDF generation completed successfully');
157-
// Clean up
158-
if (tempDiv) {
159-
document.body.removeChild(tempDiv);
160-
}
164+
}}
165+
}}).toPdf().output('datauristring');
166+
}}).then(function(dataUri) {{
167+
cleanup();
161168
callback(dataUri);
162-
}).catch(function(err) {
163-
console.error('PDF generation error:', err);
164-
// Clean up if tempDiv exists
165-
if (tempDiv) {
166-
document.body.removeChild(tempDiv);
167-
}
169+
}}).catch(function(err) {{
170+
cleanup();
168171
callback('ERROR:' + err.toString());
169-
});
170-
"##;
172+
}});
173+
"##
174+
)
175+
}
171176

172177
pub(crate) fn get_html_body(offline: bool) -> String {
173178
let offline_js = offline_js_sources();

0 commit comments

Comments
 (0)