ravenrs_report_viewer/
print.rs1use ravenrs_report::Document;
4
5pub fn print(document: &Document) {
9 let html = document.print_html();
10 #[cfg(target_arch = "wasm32")]
11 print_in_browser(&html);
12 #[cfg(not(target_arch = "wasm32"))]
13 print_on_desktop(&html);
14}
15
16#[cfg_attr(target_arch = "wasm32", allow(dead_code, reason = "the web build prints from outside the page"))]
18fn with_print_dialog(html: &str) -> String {
19 let script = "<script>document.fonts.ready.then(function(){window.print()})</script></body>";
20 html.replacen("</body>", script, 1)
21}
22
23#[cfg(not(target_arch = "wasm32"))]
24fn print_on_desktop(html: &str) {
25 let path = std::env::temp_dir().join("ravenrs-report-print.html");
26 if let Err(e) = std::fs::write(&path, with_print_dialog(html)) {
27 tracing::warn!(error = %e, "could not write the print page");
28 return;
29 }
30 if let Err(e) = ravenrs_platform_native::open_with_system(&path) {
31 tracing::warn!(error = %e, "could not open the print page");
32 }
33}
34
35#[cfg(target_arch = "wasm32")]
36fn print_in_browser(html: &str) {
37 use wasm_bindgen::JsCast;
38 use wasm_bindgen::closure::Closure;
39
40 const FRAME_ID: &str = "ravenrs-print-frame";
41 let Some(doc) = web_sys::window().and_then(|w| w.document()) else { return };
42 if let Some(old) = doc.get_element_by_id(FRAME_ID) {
43 old.remove();
44 }
45 let Some(frame) =
46 doc.create_element("iframe").ok().and_then(|e| e.dyn_into::<web_sys::HtmlIFrameElement>().ok())
47 else {
48 return;
49 };
50 frame.set_id(FRAME_ID);
51 frame.set_attribute("aria-hidden", "true").ok();
52 let style = frame.style();
54 for (k, v) in [
55 ("position", "fixed"),
56 ("right", "0"),
57 ("bottom", "0"),
58 ("width", "0"),
59 ("height", "0"),
60 ("border", "0"),
61 ] {
62 style.set_property(k, v).ok();
63 }
64 let target = frame.clone();
65 let on_load = Closure::once_into_js(move || {
66 let Some(window) = target.content_window() else { return };
67 let ready = window.document().and_then(|d| d.fonts().ready().ok());
69 wasm_bindgen_futures::spawn_local(async move {
70 if let Some(ready) = ready {
71 wasm_bindgen_futures::JsFuture::from(ready).await.ok();
72 }
73 window.focus().ok();
74 window.print().ok();
75 });
76 });
77 frame.set_onload(Some(on_load.unchecked_ref()));
78 frame.set_srcdoc(html);
79 if let Some(body) = doc.body() {
80 body.append_child(&frame).ok();
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::with_print_dialog;
87
88 #[test]
89 fn the_page_asks_for_the_dialog_inside_its_body() {
90 let page = with_print_dialog("<html><body><svg></svg></body></html>");
91 assert!(page.contains("window.print()"));
92 assert!(page.ends_with("</script></body></html>"), "{page}");
93 }
94}