I would like to use the react-to-print
library to print an iframe
from my Electron app. How can I use the iframe
reference to get the correct window/element to print?
const handleElectronPrint = async (target: HTMLIFrameElement) {
// Instead of this (printing the whole page)
// let win = BrowserWindow.getFocusedWindow();
// How do I print just the referenced iframe?
// `target` iframe has id="printWindow", how to select it?
let win = BrowserWindow.getMyIframe();
// Is this the right way to do the print once we have the iframe?
const options = { printBackground: true };
win.webContents.print(options, (success, failureReason) => {
if (!success) console.log(failureReason);
console.log('Print Initiated');
});
};
<ReactToPrint
...
print={handleElectronPrint}
/>
I would like to use the react-to-print
library to print an iframe
from my Electron app. How can I use the iframe
reference to get the correct window/element to print?
const handleElectronPrint = async (target: HTMLIFrameElement) {
// Instead of this (printing the whole page)
// let win = BrowserWindow.getFocusedWindow();
// How do I print just the referenced iframe?
// `target` iframe has id="printWindow", how to select it?
let win = BrowserWindow.getMyIframe();
// Is this the right way to do the print once we have the iframe?
const options = { printBackground: true };
win.webContents.print(options, (success, failureReason) => {
if (!success) console.log(failureReason);
console.log('Print Initiated');
});
};
<ReactToPrint
...
print={handleElectronPrint}
/>
Share
Improve this question
edited Dec 26, 2021 at 18:30
Matthew Herbst
asked Aug 12, 2021 at 4:10
Matthew HerbstMatthew Herbst
32k26 gold badges90 silver badges136 bronze badges
2
- Try creating a BrowserWindow, and assign the iframe to it temporarily. then print and delete the browserwindow? – Base64__ Commented Dec 27, 2021 at 6:50
- @Base64__ I have no knowledge of Electron, why I'm asking the question heh. If you could provide an working example, possibly as an answer, that would be appreciated – Matthew Herbst Commented Dec 28, 2021 at 14:53
3 Answers
Reset to default 12 +300You need to convert the iframe object to Data URL. And load the URL in a new hidden BrowserWindow object.
Build data URL in Renderer process and send the URL to Main process using preload. In main process do the BrowserWindow.loadURL and printing.
App.js
// Send print request to the Main process
this.handlePrint = function (target) {
return new Promise(() => {
console.log('forwarding print request to the main process...');
// convert the iframe into data url
// https://developer.mozilla/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
let data = target.contentWindow.document.documentElement.outerHTML;
//console.log(data);
var blob = new Blob([data], { type: 'text/html;charset=utf-8' });
var url = URL.createObjectURL(blob);
window.electronAPI.printComponent(url, (response) => {
console.log('Main: ', response);
});
});
};
main.js
// List of all options at -
// https://www.electronjs/docs/latest/api/web-contents#contentsprintoptions-callback
const printOptions = {
silent: false,
printBackground: true,
color: true,
margin: {
marginType: 'printableArea',
},
landscape: false,
pagesPerSheet: 1,
collate: false,
copies: 1,
header: 'Page header',
footer: 'Page footer',
};
ipcMain.handle('printComponent', (event, url) => {
let win = new BrowserWindow({ show: false });
win.loadURL(url);
win.webContents.on('did-finish-load', () => {
win.webContents.print(printOptions, (success, failureReason) => {
console.log('Print Initiated in Main...');
if (!success) console.log(failureReason);
});
});
return 'done in main';
});
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
printComponent: async (url, callback) => {
let response = await ipcRenderer.invoke('printComponent', url);
callback(response);
},
});
Here is the list of all print options. Some options like page size, margins, orientation can be set in CSS @page rule refer App.css in my demo app.
Here is demo app on GitHub electron-react-to-print-demo.
Print Preview: There is no, Chrome browser style, inbuilt print preview feature due to these reasons. We need to implement our own workaround. Like print to PDF and show pdf in new window:
//handle preview
ipcMain.handle('previewComponent', (event, url) => {
let win = new BrowserWindow({ title: 'Preview', show: false, autoHideMenuBar: true });
win.loadURL(url);
win.webContents.once('did-finish-load', () => {
win.webContents.printToPDF(printOptions).then((data) => {
let buf = Buffer.from(data);
var data = buf.toString('base64');
let url = 'data:application/pdf;base64,' + data;
win.webContents.on('ready-to-show', () => {
win.show();
win.setTitle('Preview');
});
win.webContents.on('closed', () => win = null;);
win.loadURL(url);
})
.catch((error) => {
console.log(error);
});
});
return 'shown preview window';
});
I've added above preview feature in electron-react-to-print-demo.
I am writing this answer to address a minor issue that you might encounter when following the advice in the Hutt's answer. The character encoding can be lost when creating the blob, which can cause problems when printing non-Latin characters. To prevent this, you should specify the encoding when creating the blob, like this:
const blob = new Blob([data], { type: "text/html;charset=utf-8" });
Adding on to the previous answer: it would be good idea to close the window programmatically, even when show: false
. It will take up resources. win?.close()
;
While minimized or hidden, BrowserWindows still consume some system resources like memory and processing power.