Skip to content

Commit 077c0c6

Browse files
committed
more robust pdf generation on windows
Signed-off-by: Andrei Gherghescu <[email protected]>
1 parent a732160 commit 077c0c6

File tree

2 files changed

+118
-97
lines changed

2 files changed

+118
-97
lines changed

plotly_static/src/template.rs

Lines changed: 117 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -60,108 +60,130 @@ pub(crate) fn pdf_export_js_script(timeout_ms: u32) -> String {
6060
6161
const graph_div = document.getElementById('plotly-html-element');
6262
63-
// Check if html2pdf is available
64-
if (typeof html2pdf === 'undefined') {{
65-
console.error('html2pdf library not available');
66-
callback('ERROR:html2pdf library not loaded');
67-
return;
68-
}}
63+
// Check if html2pdf is available and wait for it to load
64+
const waitForHtml2Pdf = (maxWaitMs = 5000) => {{
65+
return new Promise((resolve, reject) => {{
66+
const startTime = Date.now();
67+
68+
const checkLibrary = () => {{
69+
if (typeof html2pdf !== 'undefined' && html2pdf) {{
70+
console.log('html2pdf library loaded successfully');
71+
resolve();
72+
}} else if (Date.now() - startTime > maxWaitMs) {{
73+
console.error('html2pdf library failed to load within timeout');
74+
reject(new Error('html2pdf library not loaded within timeout'));
75+
}} else {{
76+
setTimeout(checkLibrary, 100);
77+
}}
78+
}};
79+
80+
checkLibrary();
81+
}});
82+
}};
6983
70-
let tempDiv = null;
84+
// Wait for html2pdf to be available before proceeding
85+
waitForHtml2Pdf().then(() => {{
86+
console.log('html2pdf library is ready, starting PDF export...');
87+
88+
let tempDiv = null;
7189
72-
const cleanup = () => {{
73-
if (tempDiv) {{
74-
document.body.removeChild(tempDiv);
75-
}}
76-
}};
90+
const cleanup = () => {{
91+
if (tempDiv) {{
92+
document.body.removeChild(tempDiv);
93+
}}
94+
}};
7795
78-
Plotly.newPlot(graph_div, plot).then(function() {{
79-
return Plotly.toImage(graph_div, {{
80-
format: format,
81-
width: width,
82-
height: height,
83-
}});
84-
}}).then(function(dataUrl) {{
85-
console.log('Plotly image generated successfully');
86-
console.log('SVG data URL length:', dataUrl.length);
87-
console.log('SVG data URL preview:', dataUrl.substring(0, 200) + '...');
88-
console.log('PDF dimensions (px):', width, 'x', height);
96+
Plotly.newPlot(graph_div, plot).then(function() {{
97+
return Plotly.toImage(graph_div, {{
98+
format: format,
99+
width: width,
100+
height: height,
101+
}});
102+
}}).then(function(dataUrl) {{
103+
console.log('Plotly image generated successfully');
104+
console.log('SVG data URL length:', dataUrl.length);
105+
console.log('SVG data URL preview:', dataUrl.substring(0, 200) + '...');
106+
console.log('PDF dimensions (px):', width, 'x', height);
89107
90-
// Create a temporary div for the image
91-
tempDiv = document.createElement('div');
92-
tempDiv.style.width = width + 'px';
93-
tempDiv.style.height = height + 'px';
94-
tempDiv.style.background = 'white';
95-
tempDiv.style.position = 'fixed';
96-
tempDiv.style.top = '0px';
97-
tempDiv.style.left = '0px';
98-
tempDiv.style.margin = '0px';
99-
tempDiv.style.padding = '0px';
100-
tempDiv.style.overflow = 'hidden';
101-
tempDiv.style.boxSizing = 'border-box';
102-
tempDiv.style.zIndex = '9999';
103-
tempDiv.style.display = 'block';
104-
document.body.appendChild(tempDiv);
108+
// Create a temporary div for the image
109+
tempDiv = document.createElement('div');
110+
tempDiv.style.width = width + 'px';
111+
tempDiv.style.height = height + 'px';
112+
tempDiv.style.background = 'white';
113+
tempDiv.style.position = 'fixed';
114+
tempDiv.style.top = '0px';
115+
tempDiv.style.left = '0px';
116+
tempDiv.style.margin = '0px';
117+
tempDiv.style.padding = '0px';
118+
tempDiv.style.overflow = 'hidden';
119+
tempDiv.style.boxSizing = 'border-box';
120+
tempDiv.style.zIndex = '9999';
121+
tempDiv.style.display = 'block';
122+
document.body.appendChild(tempDiv);
105123
106-
// Use simple img approach with SVG data URL
107-
console.log('Using SVG data URL directly with img element');
108-
const img = document.createElement('img');
109-
img.src = dataUrl;
110-
img.style.width = '100%';
111-
img.style.height = '100%';
112-
img.style.display = 'block';
113-
img.style.objectFit = 'contain';
114-
img.style.maxWidth = '100%';
115-
img.style.maxHeight = '100%';
116-
img.style.verticalAlign = 'top';
117-
img.style.boxSizing = 'border-box';
118-
tempDiv.appendChild(img);
124+
// Use simple img approach with SVG data URL
125+
console.log('Using SVG data URL directly with img element');
126+
const img = document.createElement('img');
127+
img.src = dataUrl;
128+
img.style.width = '100%';
129+
img.style.height = '100%';
130+
img.style.display = 'block';
131+
img.style.objectFit = 'contain';
132+
img.style.maxWidth = '100%';
133+
img.style.maxHeight = '100%';
134+
img.style.verticalAlign = 'top';
135+
img.style.boxSizing = 'border-box';
136+
tempDiv.appendChild(img);
119137
120-
// Wait for the image to load
121-
return new Promise(function(resolve) {{
122-
img.onload = function() {{
123-
console.log('SVG image loaded successfully');
124-
// Additional delay to ensure image is fully rendered
125-
// Brief delay to ensure image is fully rendered
126-
setTimeout(resolve, {timeout_ms});
127-
}};
128-
img.onerror = function() {{
129-
cleanup();
130-
callback('ERROR:Failed to load SVG image');
131-
}};
138+
// Wait for the image to load
139+
return new Promise(function(resolve) {{
140+
img.onload = function() {{
141+
console.log('SVG image loaded successfully');
142+
// Additional delay to ensure image is fully rendered
143+
// Brief delay to ensure image is fully rendered
144+
setTimeout(resolve, {timeout_ms});
145+
}};
146+
img.onerror = function() {{
147+
cleanup();
148+
callback('ERROR:Failed to load SVG image');
149+
}};
150+
}});
151+
}}).then(function() {{
152+
console.log('Starting PDF generation...');
153+
return html2pdf().from(tempDiv).set({{
154+
margin: 0,
155+
filename: 'plotly-plot.pdf',
156+
image: {{ type: 'jpeg', quality: 1}},
157+
html2canvas: {{
158+
scale: scale,
159+
backgroundColor: '#fff',
160+
useCORS: true,
161+
allowTaint: true,
162+
logging: true,
163+
width: width,
164+
height: height,
165+
imageTimeout: 15000,
166+
removeContainer: true,
167+
foreignObjectRendering: {foreign_object_rendering},
168+
scrollY: 0,
169+
scrollX: 0
170+
}},
171+
jsPDF: {{
172+
unit: 'px',
173+
format: [width, height],
174+
orientation: width > height ? 'landscape' : 'portrait',
175+
compress: true
176+
}}
177+
}}).toPdf().output('datauristring');
178+
}}).then(function(dataUri) {{
179+
cleanup();
180+
callback(dataUri);
181+
}}).catch(function(err) {{
182+
cleanup();
183+
callback('ERROR:' + err.toString());
132184
}});
133-
}}).then(function() {{
134-
console.log('Starting PDF generation...');
135-
return html2pdf().from(tempDiv).set({{
136-
margin: 0,
137-
filename: 'plotly-plot.pdf',
138-
image: {{ type: 'jpeg', quality: 1}},
139-
html2canvas: {{
140-
scale: scale,
141-
backgroundColor: '#fff',
142-
useCORS: true,
143-
allowTaint: true,
144-
logging: true,
145-
width: width,
146-
height: height,
147-
imageTimeout: 15000,
148-
removeContainer: true,
149-
foreignObjectRendering: {foreign_object_rendering},
150-
scrollY: 0,
151-
scrollX: 0
152-
}},
153-
jsPDF: {{
154-
unit: 'px',
155-
format: [width, height],
156-
orientation: width > height ? 'landscape' : 'portrait',
157-
compress: true
158-
}}
159-
}}).toPdf().output('datauristring');
160-
}}).then(function(dataUri) {{
161-
cleanup();
162-
callback(dataUri);
163-
}}).catch(function(err) {{
164-
cleanup();
185+
}}).catch((err) => {{
186+
console.error('Failed to load html2pdf library:', err);
165187
callback('ERROR:' + err.toString());
166188
}});
167189
"##

plotly_static/src/webdriver.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ impl WebDriver {
221221
}
222222
#[cfg(target_os = "windows")]
223223
{
224-
self.spawn_with_fallback(driver_path, port, e, &command)
224+
self.spawn_with_fallback(driver_path, port, e)
225225
}
226226
}
227227
}
@@ -234,7 +234,6 @@ impl WebDriver {
234234
driver_path: &PathBuf,
235235
port: u32,
236236
original_error: std::io::Error,
237-
original_command: &Command,
238237
) -> Result<Child> {
239238
// If CREATE_NO_WINDOW fails, try without any special flags
240239
error!("Failed to spawn with CREATE_NO_WINDOW: {original_error}");

0 commit comments

Comments
 (0)