# 九、打印

很多情况下程序中使用的打印都是用户无感知的。并且想要灵活的控制打印内容,往往需要借助打印机给我们提供的api再进行开发,这种开发方式非常繁琐,并且开发难度较大。第一次在业务中用到Electron其实就是用到它的打印功能,这里就多介绍一些。

Electron提供的打印 api 可以非常灵活的控制打印设置的显示,并且可以通过 html 来书写打印内容。Electron提供了两种方式进行打印,一种是直接调用打印机打印,一种是打印到pdf

并且有两种对象可以调用打印:

  • 通过windowwebcontent对象,使用此种方式需要单独开出一个打印的窗口,可以将该窗口隐藏,但是通信调用相对复杂。
  • 使用页面的webview元素调用打印,可以将webview隐藏在调用的页面中,通信方式比较简单。

上面两种方式同时拥有printprintToPdf方法。

# 9.1 调用系统打印

contents.print([options], [callback])
1

打印配置(options)中只有简单的三个配置:

  • silent:打印时是否不展示打印配置(是否静默打印)
  • printBackground:是否打印背景
  • deviceName:打印机设备名称

首先要将我们使用的打印机名称配置好,并且要在调用打印前首先要判断打印机是否可用。

使用webContentsgetPrinters方法可获取当前设备已经配置的打印机列表,注意配置过不是可用,只是在此设备上安装过驱动。

通过getPrinters获取到的打印机对象:https://electronjs.org/docs/api/structures/printer-info

我们这里只管关心两个,namestatusstatus0时表示打印机可用。

print的第二个参数callback是用于判断打印任务是否发出的回调,而不是打印任务完成后的回调。所以一般打印任务发出,回调函数即会调用并返回参数true。这个回调并不能判断打印是否真的成功了。

if (this.state.curretnPrinter) {
  mainWindow.webContents.print(
    {
      silent: silent,
      printBackground: true,
      deviceName: this.state.curretnPrinter,
    },
    () => {}
  );
} else {
  remote.dialog.showErrorBox("错误", "请先选择一个打印机!");
}
1
2
3
4
5
6
7
8
9
10
11
12

# 9.2 打印到 PDF

printToPdf的用法基本和print相同,但是print的配置项非常少,而printToPdf则扩展了很多属性。这里翻了一下源码发现还有很多没有被贴进 api 的,大概有三十几个包括可以对打印的 margin,打印页眉页脚等进行配置。

contents.printToPDF(options, callback);
1

callback函数在打印失败或打印成功后调用,可获取打印失败信息或包含PDF数据的缓冲区。

const pdfPath = path.join(os.tmpdir(), "webviewPrint.pdf");
const webview = document.getElementById("printWebview");
const renderHtml = "我是被临时插入webview的内容...";
webview.executeJavaScript(
  "document.documentElement.innerHTML =`" + renderHtml + "`;"
);
webview.printToPDF({}, (err, data) => {
  console.log(err, data);
  fs.writeFile(pdfPath, data, (error) => {
    if (error) throw error;
    shell.openExternal(`file://${pdfPath}`);
    this.setState({ webviewPdfPath: pdfPath });
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这个例子中的打印是使用webview完成的,通过调用executeJavaScript方法可动态向webview插入打印内容。

# 9.3 两种打印方案的选择

上面提到,使用webviewwebcontent都可以调用打印功能,使用webcontent打印,首先要有一个打印窗口,这个窗口不能随时打印随时创建,比较耗费性能。可以将它在程序运行时启动好,并做好事件监听。

此过程需和调用打印的进行做好通信,大致过程如下:

可见通信非常繁琐,使用webview进行打印可实现同样的效果但是通信方式会变得简单,因为渲染进程和webview通信不需要经过主进程,通过如下方式即可:

  const webview = document.querySelector('webview')
  webview.addEventListener('ipc-message', (event) => {
    console.log(event.channel)
  })
  webview.send('ping')const {ipcRenderer} = require('electron')
  ipcRenderer.on('ping', () => {
    ipcRenderer.sendToHost('pong')
  })
1
2
3
4
5
6
7
8
9
10

之前专门为ELectron打印写过一个DEMOelectron-print-demo (opens new window)有兴趣可以clone下来看一下。

# 9.4 打印功能封装

下面是几个针对常用打印功能的工具函数封装。

/**
 * 获取系统打印机列表
 */
export function getPrinters() {
  let printers = [];
  try {
    const contents = remote.getCurrentWindow().webContents;
    printers = contents.getPrinters();
  } catch (e) {
    console.error("getPrintersError", e);
  }
  return printers;
}
/**
 * 获取系统默认打印机
 */
export function getDefaultPrinter() {
  return getPrinters().find((element) => element.isDefault);
}
/**
 * 检测是否安装了某个打印驱动
 */
export function checkDriver(driverMame) {
  return getPrinters().find((element) =>
    (element.options["printer-make-and-model"] || "").includes(driverMame)
  );
}
/**
 * 根据打印机名称获取打印机对象
 */
export function getPrinterByName(name) {
  return getPrinters().find((element) => element.name === name);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 小结

希望你阅读本篇文章后可以达到以下几点:

  • 了解Electron的基本运行原理
  • 掌握Electron开发的核心基础知识
  • 了解Electron关于弹框、打印、保护、打包等功能的基本使用

文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。