Skip to content

Electron 静默打印

Posted on:2021年2月26日 at 10:13

源码

打印 HTML 格式

main 进程中

const path = require("path");
const { BrowserWindow, app, ipcMain } = require("electron");

const isPrdEnv = process.env.NODE_ENV === "production";
const staticPath = isPrdEnv ? "./static" : "../../../../static"; // 根据当前代码的js相对static文件夹路径

let printWindow = null;
const url = `file://${path.resolve(__dirname, `${staticPath}/print.html`)}`;

app.whenReady().then(() => {
  printWindow = new BrowserWindow({
    show: false,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    },
  });
  printWindow.loadURL(url);
});

/**
 * 静默打印html
 * @Param content Html字符串
 * @Param deviceName 打印机名称
 * @return promise
 * */
const htmlToPrint = (content, deviceName, margin) => {
  return new Promise((resolve, reject) => {
    if (!printWindow) return reject("请等待控件加载完成后重试");
    const htmlPrintingListener = () => {
      printWindow.webContents.print(
        {
          silent: true,
          printBackground: false,
          deviceName,
        },
        (success, failureReason) => {
          ipcMain.removeListener("htmlPrinting", htmlPrintingListener);
          if (success) resolve(true);
          else reject("打印失败");
        }
      );
    };

    printWindow.webContents.send("htmlPrint", { content, margin, deviceName });
    ipcMain.on("htmlPrinting", htmlPrintingListener);
  });
};

export default htmlToPrint;

static 文件夹下新建 print.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Document</title>
  </head>

  <body>
    <div id="container"></div>
  </body>
  <script>
    //引入ipcRenderer对象
    const { ipcRenderer } = require("electron");
    ipcRenderer.on("htmlPrint", (e, content) => {
      //接收响应
      document.querySelector("#container").innerHTML = content;
      ipcRenderer.send("htmlPrinting"); //向webview所在页面的进程传达消息
    });
  </script>
</html>

打印网络 PDF

打印本地 PDF 同理更简单, 未作兼容性处理,详细请看这篇文章,这里需要借助一个第三方软件SumatraPDF 去官网下压缩包解压后放到 static 文件夹下就行

main 进程中

const path = require("path");
const https = require("https");
const fs = require("fs");
const cp = require("child_process");

const isPrdEnv = process.env.NODE_ENV === "production";
const staticPath = isPrdEnv ? "./static" : "../../../../static"; // 根据当前代码的js相对static文件夹路径

/*
 * 处理await 的异常捕获
 * */
const awaitWrapper = promise =>
  promise.then(result => [null, result]).catch(error => [error, null]);

/*
 * 生成随机字符串
 * */
const randomString = () => Math.random().toString(36).slice(-6);

/*
 * 获取网络pdf的buffer
 * */
const getFileBuffer = url => {
  return new Promise((resolve, reject) => {
    https.get(url, response => {
      const chunks = [];
      let size = 0;
      response.on("data", chunk => {
        chunks.push(chunk);
        size += chunk.length;
      });
      response.on("end", () => {
        const buffer = Buffer.concat(chunks, size);
        resolve(buffer);
      });
    });
  });
};

/*
 * 将buffer保存为本地临时文件
 * */
const savePdf = buffer => {
  return new Promise((resolve, reject) => {
    const pdfUrl = path.resolve(
      __dirname,
      `${staticPath}/${randomString()}.pdf`
    );
    fs.writeFile(pdfUrl, buffer, { encoding: "utf8" }, err => {
      if (err) {
        reject("缓存pdf打印文件失败");
      } else {
        resolve(pdfUrl);
      }
    });
  });
};

/*
 * 调用SumatraPDF 执行pdf打印
 * */
const executePrint = (pdfPath, deviceName) => {
  return new Promise((resolve, reject) => {
    cp.exec(
      `SumatraPDF.exe -print-to "${deviceName}"  "${pdfPath}"`,
      {
        windowsHide: true,
        cwd: path.resolve(__dirname, staticPath),
      },
      e => {
        if (e) {
          reject(`${url}${deviceName}上打印失败`);
        } else {
          resolve(true);
        }
        /* 打印完成后删除创建的临时文件 */
        fs.unlink(pdfPath, Function.prototype);
      }
    );
  });
};

/*
 * 静默打印pdf
 * */
const pdfToPrint = (url, deviceName) => {
  return new Promise(async (resolve, reject) => {
    /* 根据url获取buffer并返回,如果获取失败就直接reject */
    const [bufferError, buffer] = await awaitWrapper(getFileBuffer(url));
    if (bufferError) return reject("获取网络pdf文件信息失败");
    /* 根据buffer将文件缓存到本地并返回临时pdf文件路径,如果存储失败就直接reject */
    const [pdfPathError, pdfPath] = await awaitWrapper(savePdf(buffer));
    if (pdfPathError) return reject(pdfPathError);
    /* 根据临时pdf文件路径 和打印机名称来执行打印*/
    const [execPrintError, printResult] = await awaitWrapper(
      executePrint(pdfPath, deviceName)
    );
    if (execPrintError) {
      reject(execPrintError);
    } else {
      resolve(printResult);
    }
  });
};

关键事项

package.json 中

1.打包 static 目录的文件没有打包进去,需要在 package.json 里面添加 extraResources 额外资源 2.打包后远程下载 pdf 无法放入/static 下,原因是 electron-vue 默认是用 asar 打包,而 asar 只能读取不能写入,所以需要远程打印 pdf 就不能打包成 asar

   "win": {
         "icon": "dist/electron/static/icon2.ico",
         "extraResources": [
            "./static/*.html",
            "./static/*.txt",
            "./static/*.exe",
            "./static/*.pdf"
         ],
         "asar": false
    }