Electron: print iframe given reference to it
Asked Answered
D

3

8

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}
/>
Deviationism answered 12/8, 2021 at 4:10 Comment(2)
Try creating a BrowserWindow, and assign the iframe to it temporarily. then print and delete the browserwindow?Indoeuropean
@Indoeuropean 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 appreciatedDeviationism
D
12

You 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.org/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.org/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: enter image description here

//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.

Danner answered 30/12, 2021 at 17:1 Comment(2)
This is absolutely fantastic, and exactly what I was looking for, thank you! One quick follow-up: is there a way to get Electron to show a preview of the print similar to what a browser does?Deviationism
Unfortunately they haven't used the print APIs from chromium. All are suggesting same workaround of showing printed pdf in a new window as I've discussed in the updated answer.Danner
D
1

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" });
Dedicate answered 20/5 at 11:8 Comment(0)
G
0

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.

Granulation answered 16/4 at 7:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.