ElectronJS is an Open Source Framework used for building Cross-Platform native desktop applications using web technologies such as HTML, CSS, and JavaScript which are capable of running on Windows, macOS, and Linux operating systems. It combines the Chromium engine and NodeJS into a Single Runtime.
In certain desktop applications, developers would like to provide a feature wherein the user can download or print the contents from within the application. For example, in a banking application, the user would like to print his/her account statement being displayed on the screen. Electron, in addition to saving the contents as a PDF file, also provides a way by which we can directly print the contents using the BrowserWindow object and the webContents property. The webContents property provides us with certain Instance Events and methods by which we can either print the contents of the BrowserWindow Instance being displayed, print the contents of a remote URL or print a file from the local system. This tutorial will demonstrate how to print content in Electron.
We assume that you are familiar with the prerequisites as covered in the above-mentioned link. For Electron to work, node and npm need to be pre-installed in the system.
- Project Structure:
Example: We will start by building the basic Electron Application by following the given steps.
- Step 1: Navigate to an Empty Directory to setup the project, and run the following command,
npm init
To generate the package.json file. Install Electron using npm if it is not installed.
npm install electron --save
This command will also create the package-lock.json file and install the required node_modules dependencies. Create the assets folder according to the project structure.
package.json:{ "name": "electron-print", "version": "1.0.0", "description": "Print Files in Electron ", "main": "main.js", "scripts": { "start": "electron ." }, "keywords": [ "electron" ], "author": "Radhesh Khanna", "license": "ISC", "dependencies": { "electron": "^8.2.5" } }
- Step 2: Create a main.js file according to the project structure. This file is the Main Process and acts as an entry point into the application. Copy the Boilerplate code for the main.js file as given in the following link. We have modified the code to suit our project needs.
main.js:
const { app, BrowserWindow } = require(
'electron'
)
function
createWindow () {
// Create the browser window.
const win =
new
BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration:
true
}
})
// Load the index.html of the app.
win.loadFile(
'src/index.html'
)
// Open the DevTools.
win.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
// This method is equivalent to 'app.on('ready', function())'
app.whenReady().then(createWindow)
// Quit when all windows are closed.
app.on(
'window-all-closed'
, () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if
(process.platform !==
'darwin'
) {
app.quit()
}
})
app.on(
'activate'
, () => {
// On macOS it's common to re-create a window in the
// app when the dock icon is clicked and there are no
// other windows open.
if
(BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
// In this file, you can include the rest of your
// app's specific main process code. You can also
// put them in separate files and require them here.
- Step 3: Create the index.html file and index.js file within the src directory. We will also copy the boilerplate code for the index.html file from the above-mentioned link. We have modified the code to suit our project needs.
index.html:
<!DOCTYPE html>
<
html
>
<
head
>
<
meta
charset
=
"UTF-8"
>
<
title
>Hello World!</
title
>
/security#csp-meta-tag -->
<
meta
http-equiv
=
"Content-Security-Policy"
content
=
"script-src 'self' 'unsafe-inline';"
/>
</
head
>
<
body
>
<
h1
>Hello World!</
h1
>
We are using node
<
script
>
document.write(process.versions.node)
</
script
>, Chrome
<
script
>
document.write(process.versions.chrome)
</
script
>, and Electron
<
script
>
document.write(process.versions.electron)
</
script
>.
<!-- Adding Individual Renderer Process JS File -->
<
script
src
=
"index.js"
></
script
>
</
body
>
</
html
>
- Output: At this point, our basic Electron Application is set up. To launch the Electron Application, run the Command:
npm start
Printing in Electron: The BrowserWindow Instance and webContents Property are part of the Main Process. To import and use BrowserWindow in the Renderer Process, we will be using Electron remote module. For more details on the remote module, Refer this link.
- Approach 1: Print the contents of the current active BrowserWindow Instance.
The webContents.print(options, callback) method prints the BrowserWindow contents with Chromium’s preview printing settings. This method implements a callback function. It takes in the following parameters. For more detailed information on webContents.print() method, Refer this link.
- options: Object (Optional) It takes in the following parameters,
- silent: Boolean (Optional) If this value is set to true, the application will not prompt the user for the printer settings and configurations. It will take values as set in the object or else default printer settings. Default value is false.
- printBackground: Boolean (Optional) It takes in the Background Color and Image of the web page, if any. Default value is false.
- deviceName: String (Optional) Set the Printer Device name to use. Must be the System defined name as recognized by the printer drivers.
- color: Boolean (Optional) Sets whether the printed page will be in Color or Grayscale. Default value is true.
- landscape: Boolean (Optional) Defines the mode of the printed page. This value defines whether the web page should be printed in landscape mode. Default value is false signifying portrait mode.
- scaleFactor: Integer (Optional) The scale factor of the web page. This value should not be changed until necessary.
- pagesPerSheet: Integer (Optional) The number of web pages that should be printed per actual page. The web pages will be visible as slides on the actual printed page.
- collate: Boolean (Optional) Defines whether the page should be collated. Default value is false. Collate refers to arranging of web pages into a pre-determined sequence. Collate creates consistent, logical sets from multiple different parts. For example, Arranging the pages in sequential order of page numbering.
- copies: Integer (Optional) The number of copies of the document set that need to be printed.
- duplexMode: String (Optional) Set the duplex mode of the printed web page. Values can be simplex, shortEdge or longEdge. shortEdge defines that you turn the page from the width of the paper such as in traditional notepads.
- header: String (Optional) The custom header on the printed copy of the web page.
- footer: String (Optional) The custom footer on the printed copy of the web page.
- dpi: Object (Optional) It stands for Dots Per Inch. It is a measure of density of a printed page. It takes in the following parameters,
- horizontal: Integer (Optional) The horizontal dpi.
- vertical: Integer (Optional) The vertical dpi.
- margins: Object (Optional) It takes in the following parameters,
- marginType: String (Optional) Values can be default, none, printableArea, or custom. If custom is chosen. We also need to define the following values,
- top: Integer (Optional) The top margin defined in pixels.
- left: Integer (Optional) The left margin defined in pixels.
- bottom: Integer (Optional) The bottom margin defined in pixels.
- right: Integer (Optional) The right margin defined in pixels.
All of these values are part of a single object.
When silent property is set to true, Electron will pick the system’s default printer if the deviceName property is not defined and the default system settings for printing. If the silent property is set to false, then Electron will open a dialog displaying all default system printer settings. All of the other values defined in the object can be adjusted and changed from that custom dialog.
Note: To force this method to print to a new page use the page-break-before: always; property in CSS. There are some bugs associated with the silent property on some system environments as per the lastest version of Electron. It causes some issues when printing different files such as PDF files when the silent property is set to true. - options: Object (Optional) It takes in the following parameters,
- callback: function (Optional) It consists of two values,
- success: Boolean Indicates whether the print call was successful or not.
- failureReason: String Description of the Error if print call was unsuccessful.
To get the current BrowserWindow Instance in the Renderer Process, we can use some of the Static Methods provided by the BrowserWindow object.
- BrowserWindow.getAllWindows(): This method returns an Array of active/opened BrowserWindow Instances. In this application, we have only one active BrowserWindow Instance and it can be directly referred from the Array as shown in the code.
- BrowserWindow.getFocusedWindow(): This method returns the BrowserWindow Instance which is focused in the Application. If no current BrowserWindow Instance is found, it returns null. In this application, we only have one active BrowserWindow Instance and it can be directly referred using this method as shown in the code.
index.html: Add the following snippet in that file.
< br >< br > < button id = "current" > Print Current Content of Page </ button > |
index.js: Add the following snippet in that file.
const electron = require( 'electron' ) // Importing BrowserWindow from Main const BrowserWindow = electron.remote.BrowserWindow; var current = document.getElementById( 'current' ); var options = { silent: false , printBackground: true , color: false , margin: { marginType: 'printableArea' }, landscape: false , pagesPerSheet: 1, collate: false , copies: 1, header: 'Header of the Page' , footer: 'Footer of the Page' } current.addEventListener( 'click' , (event) => { let win = BrowserWindow.getFocusedWindow(); // let win = BrowserWindow.getAllWindows()[0]; win.webContents.print(options, (success, failureReason) => { if (!success) console.log(failureReason); console.log( 'Print Initiated' ); }); }); |
Output:
Printable Page:
In this case we have created a new BrowserWindow Instance and set the show property to false. Hence the newly created window will never be shown. We have used the win.loadURL(path) method to load the contents of the External URL in the BrowserWindow. The url path can be a remote address specified by http:// protocol or a path to a file in the local System specified by using the file:// protocol. This method returns a Promise and it is resolved when the page has finished loading and the did-finish-load Event of webContents property is Emitted. For more detailed Information, Refer this link.
The did-finish-load Instance Event belongs to the webContents Property. It is emitted when the navigation is done and the page is completely loaded. This happens when the spinner of the page has stopped spinning, and the onload event has been dispatched. In case, this event emitter is not used and the webContents.print() method is called, the printed page will be a blank document since the content did not finish loading in the BrowserWindow.
index.html: Add the following snippet in that file.
< br >< br > < button id = "url" >Print Google.com Homepage</ button > |
index.js: Add the following snippet in that file.
const electron = require( 'electron' ) // Importing BrowserWindow from Main const BrowserWindow = electron.remote.BrowserWindow; var url = document.getElementById( 'url' ); var options = { silent: false , printBackground: true , color: false , margin: { marginType: 'printableArea' }, landscape: false , pagesPerSheet: 1, collate: false , copies: 1, header: 'Header of the Page' , footer: 'Footer of the Page' } url.addEventListener( 'click' , (event) => { // Defining a new BrowserWindow Instance let win = new BrowserWindow({ show: false , webPreferences: { nodeIntegration: true } }); win.webContents.on( 'did-finish-load' , () => { win.webContents.print(options, (success, failureReason) => { if (!success) console.log(failureReason); console.log( 'Print Initiated' ); }); }); }); |
Output: